[BZOJ2333][SCOI2011]棘手的操作(可并堆||线段树+离线)
2017-01-07 15:18
417 查看
题目描述
传送门题解
对没错总会有一道题分到这个编号…233333第一眼想到线段树,处理生成树(重新编号)使在任何时间在同一个连通块里的点都在一个连续的区间,然后搞搞搞就可以了
然而要是换成可并堆的话操作还真是棘手啊…
首先我们要维护两种可并堆(一个可并堆和一个splay也可以…),以下简称a堆和b堆
a堆是按照题目中的操作合并的,也就是说有若干个小堆
b堆只有一个堆,维护的是所有a堆的堆顶(最大值),也就是说堆中的节点数等于a堆的个数
a中每一个堆顶要维护一个标记add,表示当前这个堆中键值+add为实际值
维护全局ADD,表示总体加的值
U x y :在a中找到x和y中的根fx和fy,在b中删除fx和fy;在a中合并x和y,得到新的根top;将top插入b
A1 x v :在a中找到x的根fx,在a中删除x,在b中删除fx;修改x的键值,将x插入a,得到新根top;将top插入b
A2 x v :在a中找到x的根fx,在b中删除fx;在a中修改fx的标记,在b中修改fx的键值;在b中插入fx
A3 x :ADD+=v
F1 x :在a中找到x的根fx,key(x)+add(fx)+ADD
F2 x :在a中找到x的根fx,key(fx)+add(fx)+ADD
F3 x :找到b的根fx,key(fx)+ADD
注意:
a中每个根有标记add,堆中的键值+堆顶add才是正确值;b中没有标记,维护的键值即为实际值
U中将a堆合并时由于堆顶标记不同,需要修改某一个堆中的键值,使两堆标记相同。用启发式合并,每次暴力修改较小的堆,每个点只会被合并至多logn次
时间复杂度O(nlogn)*一坨常数
写完可并堆了之后发现线段树的写法之前从来没写过又去写了一发…
值得一做的题.
代码
可并堆#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; #define N 300005 int n,m,x,y,fx,fy,top,tmp,temp,v,ADD,lastsize,lastadd; int ls ,rs ,dis ,key ,f ,add ,par ,size ; char opt[5]; queue <int> q; void Add(int x,int v)//启发式 { if (ls[x]) Add(ls[x],v); if (rs[x]) Add(rs[x],v); key[x]+=v; return; } int find(int x)//找根 { if (x==f[x]) return x; f[x]=find(f[x]); return f[x]; } int merge(int x,int y)//合并 { if (!x) return y; if (!y) return x; if (key[x]<key[y]) swap(x,y); rs[x]=merge(rs[x],y); par[rs[x]]=x; if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]); if (!rs[x]) dis[x]=0; else dis[x]=dis[rs[x]]+1; return x; } int del(int x)//删除x,返回x原先所在堆中的根(若删除x之后堆为空,则返回0) { int p,q,re; q=par[x]; p=merge(ls[x],rs[x]); if (p) par[p]=q; if (q&&ls[q]==x) ls[q]=p; if (q&&rs[q]==x) rs[q]=p; if (!q) re=p; else re=find(q); while (q) { if (dis[ls[q]]<dis[rs[q]]) swap(ls[q],rs[q]); if (dis[rs[q]]+1==dis[q]) break; dis[q]=dis[rs[q]]+1; q=par[q]; } ls[x]=rs[x]=dis[x]=par[x]=0; return re; } namespace st { int ls ,rs ,dis ,key ,par ; int root; int merge(int x,int y)//合并 { if (!x) return y; if (!y) return x; if (key[x]<key[y]) swap(x,y); rs[x]=merge(rs[x],y); par[rs[x]]=x; if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]); if (!rs[x]) dis[x]=0; else dis[x]=dis[rs[x]]+1; return x; } void clear()//左偏树构建 { for (int i=1;i<=n;++i) q.push(i); while (!q.empty()) { x=q.front();q.pop(); if (!q.empty()) {y=q.front();q.pop();} else break; top=merge(x,y); q.push(top); } st::root=x; } int del(int x)//删除x,返回根(可能为0) { int p,q; q=par[x]; p=merge(ls[x],rs[x]); if (p) par[p]=q; if (q&&ls[q]==x) ls[q]=p; if (q&&rs[q]==x) rs[q]=p; if (!q) root=p; while (q) { if (dis[ls[q]]<dis[rs[q]]) swap(ls[q],rs[q]); if (dis[q]==dis[rs[q]]+1) break; dis[q]=dis[rs[q]]+1; p=q; q=par[q]; } ls[x]=rs[x]=dis[x]=par[x]=0; f[x]=x; return root; } } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) { scanf("%d",&key[i]); st::key[i]=key[i]; } for (int i=1;i<=n;++i) f[i]=i,size[i]=1; dis[0]=st::dis[0]=-1; st::clear(); scanf("%d",&m); for (int i=1;i<=m;++i) { scanf("%s",opt); if (opt[0]=='U') { scanf("%d%d",&x,&y); fx=find(x),fy=find(y); if (fx!=fy) { tmp=st::del(fx); tmp=st::del(fy); if (size[fx]>size[fy]) swap(x,y),swap(fx,fy); Add(fx,add[fx]-add[fy]); top=merge(fx,fy); if (fx) f[fx]=top; if (fy) f[fy]=top; size[top]=size[fx]+size[fy]; add[top]=add[fy]; temp=st::merge(tmp,top); st::root=temp; } } else if (opt[0]=='A') { if (opt[1]=='1') { scanf("%d%d",&x,&v); fx=find(x); tmp=st::del(fx); lastsize=size[fx]; lastadd=add[fx]; y=del(x); key[x]+=v; top=merge(x,y); if (x) f[x]=top; if (y) f[y]=top; size[top]=lastsize; add[top]=lastadd; st::key[top]=key[top]+add[top]; temp=st::merge(tmp,top); st::root=temp; } else if (opt[1]=='2') { scanf("%d%d",&x,&v); fx=find(x); tmp=st::del(fx); add[fx]+=v; st::key[fx]=key[fx]+add[fx]; temp=st::merge(tmp,fx); st::root=temp; } else if (opt[1]=='3') { scanf("%d",&v); ADD+=v; } } else if (opt[0]=='F') { if (opt[1]=='1') { scanf("%d",&x); fx=find(x); printf("%d\n",key[x]+add[fx]+ADD); } else if (opt[1]=='2') { scanf("%d",&x); fx=find(x); printf("%d\n",key[fx]+add[fx]+ADD); } else if (opt[1]=='3') { x=st::root; printf("%d\n",st::key[x]+ADD); } } } return 0; }
线段树+离线
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; #define N 300005 #define inf 2100000000 int n,m,fx,fy,cnt,ans; char opt [3]; int a ,x ,y ,v ,f ,size ,from ,to ,l ,r ,pt ,val ; int maxn[N*4],delta[N*4]; int find(int x) { if (x==f[x]) return x; f[x]=find(f[x]); return f[x]; } void update(int now) { maxn[now]=max(maxn[now<<1],maxn[now<<1|1]); } void pushdown(int now,int l,int r,int mid) { if (delta[now]) { maxn[now<<1]+=delta[now];delta[now<<1]+=delta[now]; maxn[now<<1|1]+=delta[now];delta[now<<1|1]+=delta[now]; delta[now]=0; } } void build(int now,int l,int r) { int mid=(l+r)>>1; if (l==r) { maxn[now]=val[l]; return; } build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now); } void change(int now,int l,int r,int lrange,int rrange,int v) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { maxn[now]+=v; delta[now]+=v; return; } pushdown(now,l,r,mid); if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,v); if (mid+1<=rrange) change(now<<1|1,mid+1,r,lrange,rrange,v); update(now); } int query(int now,int l,int r,int lrange,int rrange) { int mid=(l+r)>>1,ans=-inf; if (lrange<=l&&r<=rrange) return maxn[now]; pushdown(now,l,r,mid); if (lrange<=mid) ans=max(ans,query(now<<1,l,mid,lrange,rrange)); if (mid+1<=rrange) ans=max(ans,query(now<<1|1,mid+1,r,lrange,rrange)); return ans; } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&a[i]); for (int i=1;i<=n;++i) f[i]=i,size[i]=1; scanf("%d",&m); for (int i=1;i<=m;++i) { scanf("%s",opt[i]); if (opt[i][0]=='U') { scanf("%d%d",&x[i],&y[i]); fx=find(x[i]),fy=find(y[i]); if (fx==fy) continue; if (fx<fy) swap(x[i],y[i]),swap(fx,fy); size[fy]+=size[fx]; f[fx]=fy; from[++cnt]=fx;to[cnt]=fy; } else if (opt[i][0]=='A') { if (opt[i][1]=='1') scanf("%d%d",&x[i],&v[i]); else if (opt[i][1]=='2') scanf("%d%d",&x[i],&v[i]); else if (opt[i][1]=='3') scanf("%d",&v[i]); } else if (opt[i][0]=='F') { if (opt[i][1]=='1') scanf("%d",&x[i]); else if (opt[i][1]=='2') scanf("%d",&x[i]); } } int last=0; for (int i=1;i<=n;++i) if (find(i)==i&&size[i]>1) { l[i]=last+1; r[i]=pt[i]=last+size[i]; last+=size[i]; } for (int i=cnt;i>=1;--i) { r[from[i]]=pt[from[i]]=pt[to[i]]; l[from[i]]=r[from[i]]-size[from[i]]+1; pt[to[i]]=l[from[i]]-1; } for (int i=1;i<=n;++i) if (find(i)==i&&size[i]==1) l[i]=r[i]=++last; for (int i=1;i<=n;++i) f[i]=i,size[i]=1,val[l[i]]=a[i]; build(1,1,n); for (int i=1;i<=m;++i) { if (opt[i][0]=='U') { fx=find(x[i]),fy=find(y[i]); if (fx==fy) continue; if (fx<fy) swap(fx,fy),swap(x[i],y[i]); f[fx]=fy; size[fy]+=size[fx]; } else if (opt[i][0]=='A') { if (opt[i][1]=='1') change(1,1,n,l[x[i]],l[x[i]],v[i]); else if (opt[i][1]=='2') { fx=find(x[i]); change(1,1,n,l[fx],l[fx]+size[fx]-1,v[i]); } else if (opt[i][1]=='3') change(1,1,n,1,n,v[i]); } else if (opt[i][0]=='F') { if (opt[i][1]=='1') { ans=query(1,1,n,l[x[i]],l[x[i]]); printf("%d\n",ans); } else if (opt[i][1]=='2') { fx=find(x[i]); ans=query(1,1,n,l[fx],l[fx]+size[fx]-1); printf("%d\n",ans); } else if (opt[i][1]=='3') { ans=query(1,1,n,1,n); printf("%d\n",ans); } } } return 0; }
相关文章推荐
- BZOJ 2333 [SCOI2011]棘手的操作
- bzoj 2333: [SCOI2011]棘手的操作(线段树+离线操作)
- [bzoj2333][SCOI2011][棘手的操作]
- [BZOJ2333][SCOI2011]棘手的操作(可并堆||线段树)
- BZOJ 2333 SCOI 2011 棘手的操作 可并堆
- 【bzoj2333】[SCOI2011]棘手的操作 可并堆+STL-set
- BZOJ 2333 【SCOI2011】 棘手的操作
- 【BZOJ2333】棘手的操作(SCOI2011)-线段树+并查集+离线处理
- 【BZOJ2333】【SCOI2011】棘手的操作 可并堆+堆套堆(什么嘛,用个set就好啦)
- [可并堆] BZOJ 2333 [SCOI2011]棘手的操作
- 【bzoj2333】 SCOI2011—棘手的操作
- 【bzoj2333】 [SCOI2011]棘手的操作 可并堆+lazy标记
- 【BZOJ2333】【SCOI2011】棘手的操作
- 【BZOJ2333】【SCOI2011】棘手的操作 treap合并
- BZOJ 2333: [SCOI2011]棘手的操作
- 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树|可并堆-左偏树)
- 【bzoj2333】[SCOI2011]棘手的操作 可并堆+set
- 【左偏树】【bzoj 2333】: [SCOI2011]棘手的操作
- BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆
- [BZOJ2333][SCOI2011]棘手的操作