左偏树习题
1.板题 P3377
2.P2713
板题的双倍经验。
// Problem: P2713 罗马游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2713
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-10-28 17:40:23
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int n,m;
int a[N];
int rt[N],lc[N],rc[N],dis[N];
int find(int x){
return rt[x]==x?x:rt[x]=find(rt[x]);
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(a[y]<a[x]) swap(x,y);
rc[x]=merge(rc[x],y);
if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
dis[x]=dis[rc[x]]+1;
return x;
}
bool rm[N];
int main(){
dis[0]=-1; //0号结点看作空结点
scanf("%d",&n);
rep(i,1,n) scanf("%d",&a[i]),rt[i]=i;
scanf("%d",&m);
while(m--){
char op;
int x,y;
scanf("\n%c",&op);
scanf("%d",&x);
if(op=='M'){
scanf("%d",&y);
if(rm[x]||rm[y]) continue;
x=find(x),y=find(y);
if(x!=y) rt[x]=rt[y]=merge(x,y);
}
else{
if(rm[x]){
puts("0");continue;
}
x=find(x);
printf("%d\n",a[x]);
rm[x]=true;
rt[lc[x]]=rt[rc[x]]=rt[x]=merge(lc[x],rc[x]);
lc[x]=rc[x]=dis[x]=0;
}
}
return 0;
}
3.P1456
左偏树大顶堆,需要支持删除最小值,插入值。
大顶堆相对于小顶堆,只是 v a l l s o n ≥ v a l r s o n val_{lson}\ge val_{rson} vallson≥valrson
多组记得清0lc[i]=rc[i]=rt[i]=0
// Problem: P1456 Monkey King
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1456
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-10-28 22:45:32
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int n,m;
int a[N];
int rt[N],lc[N],rc[N],dis[N];
int find(int x){
return rt[x]==x?x:rt[x]=find(rt[x]);
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(a[x]<a[y]) swap(x,y); //这里是<(大顶堆)
rc[x]=merge(rc[x],y);
if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
dis[x]=dis[rc[x]]+1;
return x;
}
bool rm[N];
int main(){
dis[0]=-1; //0号结点看作空结点
while(~scanf("%d",&n)){
rep(i,1,n) scanf("%d",&a[i]),rt[i]=i,dis[i]=lc[i]=rc[i]=0; //多组清0
scanf("%d",&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
x=find(x),y=find(y);
if(x==y) puts("-1");
else {
a[x]>>=1,a[y]>>=1;
int rx=merge(lc[x],rc[x]); //先合并
lc[x]=rc[x]=dis[x]=0; //清除
int nrx=merge(rx,x); //再插入.
rt[x]=rt[rx]=nrx;
int ry=merge(lc[y],rc[y]); //同理
lc[y]=rc[y]=dis[y]=0;
int nry=merge(ry,y);
rt[y]=rt[ry]=nry;
rt[nrx]=rt[nry]=merge(nrx,nry);
printf("%d\n",a[rt[nrx]]);
}
}
}
return 0;
}
4.P4331
考虑两种情况:
1.单调递增,显然 b i = a i b_i=a_i bi=ai
2.单调递减,显然取 a a a的中位数。
为了方便,不妨将 a i − i , b i − i a_i-i,b_i-i ai−i,bi−i 这样 b b b只需满足不递减。
可以将 a a a按照上述情况分成若干区间,如果 x i ≤ x i + 1 x_i\le x_{i+1} xi≤xi+1不用合并,否则取两个集合的中位数即可。
为了方便 维护中位数,我们只需要一个数据结构能维护区间前 ⌈ l e n 2 ⌉ \lceil \dfrac{len}{2}\rceil ⌈2len⌉小元素即可。考虑用左偏树维护可合并大顶堆,每次 s z > ⌈ r − l + 1 2 ⌉ sz>\lceil\dfrac{r-l+1}{2}\rceil sz>⌈2r−l+1⌉ 就删除堆顶。
每个元素最多被插入、删除一次。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
struct node{
int id,l,r,sz,v;
}a[N];
int lc[N],rc[N],dis[N];
int w[N],m,b[N],n;
int merge(int x,int y){
if(!x||!y) return x+y;
if(w[x]<w[y]) swap(x,y);
rc[x]=merge(rc[x],y);
if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
dis[x]=dis[rc[x]]+1;
return x;
}
int main(){
dis[0]=-1;
scanf("%d",&n);
rep(i,1,n) scanf("%d",&w[i]),w[i]-=i;
rep(i,1,n){
a[++m]={i,i,i,1,w[i]};
while(m>1&&a[m].v<a[m-1].v){
m--;
a[m].id=merge(a[m].id,a[m+1].id);
a[m].sz+=a[m+1].sz;
a[m].r=a[m+1].r;
while(a[m].sz>(a[m].r-a[m].l+2)>>1){
a[m].sz--;
a[m].id=merge(lc[a[m].id],rc[a[m].id]);
}
a[m].v=w[a[m].id];
}
}
ll ans=0;
rep(i,1,m){
for(int j=a[i].l;j<=a[i].r;++j)
ans+=abs(a[i].v-w[j]),b[j]=a[i].v;
}
printf("%lld\n",ans);
rep(i,1,n) printf("%d ",b[i]+i);
return 0;
}