【BZOJ4712】洪水 樹鏈剖分優化DP+線段樹
阿新 • • 發佈:2017-10-05
表示 他還 efi 父親 管理員 out 接下來 到你 head
4 3 2 1
1 2
1 3
4 2
4
Q 1
Q 2
C 4 10
Q 1
1
4
【BZOJ4712】洪水
Description
小A走到一個山腳下,準備給自己造一個小屋。這時候,小A的朋友(op,又叫管理員)打開了創造模式,然後飛到山頂放了格水。於是小A面前出現了一個瀑布。作為平民的小A只好老實巴交地爬山堵水。那麽問題來了:我們把這個瀑布看成是一個n個節點的樹,每個節點有權值(爬上去的代價)。小A要選擇一些節點,以其權值和作為代價將這些點刪除(堵上),使得根節點與所有葉子結點不連通。問最小代價。不過到這還沒結束。小A的朋友覺得這樣子太便宜小A了,於是他還會不斷地修改地形,使得某個節點的權值發生變化。不過到這還沒結束。小A覺得朋友做得太絕了,於是放棄了分離所有葉子節點的方案。取而代之的是,每次他只要在某個子樹中(和子樹之外的點完全無關)。於是他找到你。Input
輸入文件第一行包含一個數n,表示樹的大小。
接下來一行包含n個數,表示第i個點的權值。 接下來n-1行每行包含兩個數fr,to。表示書中有一條邊(fr,to)。 接下來一行一個整數,表示操作的個數。 接下來m行每行表示一個操作,若該行第一個數為Q,則表示詢問操作,後面跟一個參數x,表示對應子樹的根;若為C,則表示修改操作,後面接兩個參數x,to,表示將點x的權值加上to。 n<=200000,保證任意to都為非負數Output
對於每次詢問操作,輸出對應的答案,答案之間用換行隔開。
Sample Input
44 3 2 1
1 2
1 3
4 2
Q 1
Q 2
C 4 10
Q 1
Sample Output
31
4
題解:設每個點的DP值是f[x],每個點兒子的f之和為g[x],f[x]=min(g[x],v[x])。發現,由於v[x]只能變大,所以每次操作可能導致一些點從選g[x]變成選v[x],但只能導致一個點從選v[x]變成選g[x],所以對於從選v變成選g或從選g變成選v的,我們都可以暴力搞定(均攤的思想)。具體做法:
先改v[x],如果v[x]的增長導致了f[x]的改變,則令它父親的g值+=改變值,然後遞歸它的父親:
先讓當前的g值+=該變量,如果f不變,則結束;如果原來選g,改完變成選v,那麽記錄該變量,繼續修改它的父親;如果原來選g,改完還是選g,那麽一定是:當前點的連續幾輩祖先都是原來選g改完也選g,那麽我們二分找到深度最小的祖先,然後中間的g值都能用線段樹直接修改。然後再繼續修改這個祖先的父親即可。
那麽二分的過程具體如何實現呢?需要用線段樹維護v[x]-g[x]的最小值,然後再樹剖的時候,先判斷當前鏈的鏈頂是否符合要求,如果不是,則在鏈內二分,否則在直接跳到下一條鏈上。
這樣就保證了時間復雜度是$O(nlog_n^2)$的。
#include <cstdio> #include <cstring> #include <iostream> #define lson x<<1 #define rson x<<1|1 using namespace std; const int maxn=200010; typedef long long ll; int n,m,cnt; int to[maxn<<1],next[maxn<<1],head[maxn],p[maxn],Q[maxn],dep[maxn],fa[maxn],top[maxn],siz[maxn],son[maxn]; char str[5]; ll sv[maxn<<2],ts[maxn<<2],sc[maxn<<2],v[maxn],g[maxn]; inline void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } void dfs1(int x) { siz[x]=1; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]) { fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]]; if(siz[to[i]]>siz[son[x]]) son[x]=to[i]; g[x]+=min(g[to[i]],v[to[i]]); } if(siz[x]==1) g[x]=1<<30; } void dfs2(int x,int tp) { p[x]=++p[0],Q[p[0]]=x,top[x]=tp; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]); } inline void pushdown(int x) { if(ts[x]) sc[lson]-=ts[x],sc[rson]-=ts[x],ts[lson]+=ts[x],ts[rson]+=ts[x],ts[x]=0; } void build(int l,int r,int x) { if(l==r) { sv[x]=v[Q[l]],sc[x]=v[Q[l]]-g[Q[l]]; return ; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]); } ll getg(int l,int r,int x,int a) { if(l==r) return sv[x]-sc[x]; pushdown(x); int mid=(l+r)>>1; if(a<=mid) return getg(l,mid,lson,a); return getg(mid+1,r,rson,a); } ll getv(int l,int r,int x,int a,int b) { if(l==r) return sv[x]; pushdown(x); int mid=(l+r)>>1; if(b<=mid) return getv(l,mid,lson,a,b); if(a>mid) return getv(mid+1,r,rson,a,b); return min(getv(l,mid,lson,a,b),getv(mid+1,r,rson,a,b)); } ll getc(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) return sc[x]; pushdown(x); int mid=(l+r)>>1; if(b<=mid) return getc(l,mid,lson,a,b); if(a>mid) return getc(mid+1,r,rson,a,b); return min(getc(l,mid,lson,a,b),getc(mid+1,r,rson,a,b)); } void upg(int l,int r,int x,int a,int b,ll c) { if(a<=l&&r<=b) { if(c==-1) sc[x]+=v[Q[l]]-sv[x],sv[x]=v[Q[l]]; else sc[x]-=c,ts[x]+=c; return ; } pushdown(x); int mid=(l+r)>>1; if(a<=mid) upg(l,mid,lson,a,b,c); if(b>mid) upg(mid+1,r,rson,a,b,c); sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]); } inline void change(int x,ll d) { while(x) { ll gx=getg(1,n,1,p[x]),fx=min(gx,v[x]); upg(1,n,1,p[x],p[x],d); if(gx>=v[x]) return ; if(gx+d>v[x]) d=v[x]-fx,x=fa[x]; else { if(top[x]==x) x=fa[x]; else if(getc(1,n,1,p[top[x]],p[x]-1)>=d) { upg(1,n,1,p[top[x]],p[x]-1,d); x=fa[top[x]]; } else { int l=p[top[x]],r=p[x],mid; while(l<r) { mid=(l+r)>>1; if(getc(1,n,1,mid,p[x]-1)>=d) r=mid; else l=mid+1; } if(r<p[x]) upg(1,n,1,r,p[x]-1,d); x=Q[r-1]; } } } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { n=rd(); int i,a,b; for(i=1;i<=n;i++) v[i]=rd(); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); dep[1]=1,dfs1(1),dfs2(1,1); m=rd(); build(1,n,1); for(i=1;i<=m;i++) { scanf("%s",str); if(str[0]==‘C‘) { a=rd(),b=rd(); ll gx=getg(1,n,1,p[a]),fx=min(gx,v[a]); v[a]+=b,upg(1,n,1,p[a],p[a],-1); if(min(v[a],gx)>fx) change(fa[a],min(v[a],gx)-fx); } else a=rd(),printf("%lld\n",min(v[a],getg(1,n,1,p[a]))); } return 0; }//4 4 3 2 1 1 2 1 3 4 2 4 Q 1 Q 2 C 4 10 Q 1
【BZOJ4712】洪水 樹鏈剖分優化DP+線段樹