1. 程式人生 > >【bzoj2333 & luoguP3273】棘手的操作(線段樹合併)

【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); } } }
bzoj2333 & luoguP3273