【bzoj2333 & luoguP3273】棘手的操作(线段树合并)
题目传送门:bzoj2333 luoguP3273
这操作还真“棘手”。。听说这题是可并堆题?然而我不会可并堆。于是我就写了线段数合并,然后调了一晚上,数据结构毁一生!!!QAQ……
其实这题也可以把合并强行看成树上的关系然后dfs序后直接线段树的,然而我菜啊。。看到连边就只能想到线段树合并。
首先用并查集维护图的连通性,然后对于每个连通块建一棵下标为点的编号的线段树,于是:
U=合并两棵树;
A1:单点加;
A2:区间加;
A3:因为是整体加,所以我们可以维护一个delta表示当前每个数被整体加了多少,然后输出时直接加上就好了;
F1:单点查询;
F2:区间查询;
然而还有一个F3整体查询最大值很难处理。于是我再开了一颗线段树,维护每个连通块内的最大值,修改时顺便在上面修改信息,F3操作可以直接在树上查询。
时间复杂度$ O(n \log n) $,然而常数极大。
两颗线段树+奇奇怪怪的维护方法,使写出来的代码膨胀到了3.6kB……然后,调试++,and then,(如果你写的不优美)MLE+TLE。经过了一番痛苦的调试后,终于过了。。。QAQ
又臭又长的代码:
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<ctime> #include<algorithm> #define ll long long #define inf 0x3f3f3f3f #define maxn 300010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } struct segment_tree1{ struct point{ int lc,rc,mx,delta; }sgt[20*maxn]; int tot; void add(int now,int l,int r,int x,int y,int k) { if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k; else{ int mid=(l+r)>>1; if(x<=mid){ if(!sgt[now].lc)sgt[now].lc=++tot; add(sgt[now].lc,l,mid,x,y,k); } if(mid<y){ if(!sgt[now].rc)sgt[now].rc=++tot; add(sgt[now].rc,mid+1,r,x,y,k); } sgt[now].mx=std::max(sgt[sgt[now].lc].mx,sgt[sgt[now].rc].mx); if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta; } } int getmax(int now,int l,int r,int x,int y) { if(x<=l&&r<=y)return sgt[now].mx; else{ int mid=(l+r)>>1,mx=-inf; if(x<=mid&&sgt[now].lc)mx=std::max(mx,getmax(sgt[now].lc,l,mid,x,y)); if(mid<y&&sgt[now].rc)mx=std::max(mx,getmax(sgt[now].rc,mid+1,r,x,y)); return mx+sgt[now].delta; } } void merge(int x,int y,int d) { d+=sgt[y].delta-sgt[x].delta; if(!sgt[x].lc)sgt[x].lc=sgt[y].lc,sgt[sgt[x].lc].delta+=d,sgt[sgt[x].lc].mx+=d; else if(sgt[y].lc)merge(sgt[x].lc,sgt[y].lc,d); if(!sgt[x].rc)sgt[x].rc=sgt[y].rc,sgt[sgt[x].rc].delta+=d,sgt[sgt[x].rc].mx+=d; else if(sgt[y].rc)merge(sgt[x].rc,sgt[y].rc,d); sgt[x].mx=std::max(sgt[sgt[x].lc].mx,sgt[sgt[x].rc].mx)+sgt[x].delta; } }t1; struct segment_tree2{ struct point{ int mx,delta; }sgt[4*maxn]; void add(int now,int l,int r,int x,int y,int k) { if(x<=l&&r<=y)sgt[now].delta+=k,sgt[now].mx+=k; else{ int mid=(l+r)>>1; if(x<=mid)add(now<<1,l,mid,x,y,k); if(mid<y)add(now<<1|1,mid+1,r,x,y,k); sgt[now].mx=std::max(sgt[now<<1].mx,sgt[now<<1|1].mx); if(sgt[now].mx!=-inf)sgt[now].mx+=sgt[now].delta; } } int getmax(int now,int l,int r,int x,int y) { if(x<=l&&r<=y)return sgt[now].mx; else{ int mid=(l+r)>>1,mx=-inf; if(x<=mid)mx=std::max(mx,getmax(now<<1,l,mid,x,y)); if(mid<y)mx=std::max(mx,getmax(now<<1|1,mid+1,r,x,y)); return sgt[now].delta+mx; } } }t2; int rt[maxn],fa[maxn],a[maxn],delta; int n,m; char op[5]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int main() { n=read(); t1.tot=0; delta=0; t1.sgt[0].mx=t2.sgt[0].mx=-inf; for(int i=1;i<=n;i++){ a[i]=read(); rt[i]=++t1.tot; fa[i]=i; t1.add(rt[i],1,n,i,i,a[i]); t2.add(1,1,n,i,i,a[i]); } m=read(); for(int i=1;i<=m;i++){ scanf("%s",op); if(op[0]=='U'){ int x=read(),y=read(); x=find(x); y=find(y); if(x!=y){ fa[y]=x; t1.merge(rt[x],rt[y],0); t2.add(1,1,n,x,x,std::max(a[x],a[y])-a[x]); t2.add(1,1,n,y,y,-a[y]-inf); a[x]=std::max(a[x],a[y]); a[y]=-inf; } } else if(op[0]=='A'){ if(op[1]=='1'){ int x=read(),v=read(),fx=find(x); t1.add(rt[fx],1,n,x,x,v); int t=t1.getmax(rt[fx],1,n,1,n); t2.add(1,1,n,fx,fx,t-a[fx]); a[fx]=t; } else if(op[1]=='2'){ int x=read(),v=read(),fx=find(x); t1.add(rt[fx],1,n,1,n,v); t2.add(1,1,n,fx,fx,v); a[fx]+=v; } else{ int v=read(); delta+=v; } } else{ if(op[1]=='1'){ int x=read(),fx=find(x); printf("%d\n",t1.getmax(rt[fx],1,n,x,x)+delta); } else if(op[1]=='2'){ int x=read(),fx=find(x); printf("%d\n",a[fx]+delta); } else printf("%d\n",t2.getmax(1,1,n,1,n)+delta); } } }
可并堆的做法以后再补吧。。