BZOJ 4712 洪水 (线段树+树剖动态维护DP)
题目大意:略 题目传送门
数据结构好题,但据说直接上动态DP会容易处理不少,然而蒟蒻不会。一氧化碳大爷说还有一个$log$的做法,然而我只会$log^{2}$的..
考虑静态时如何处理,设$f[x]$表示堵住$x$这棵子树的最小花费,$g[x]$表示$x$所有子节点的$f[x]$总和,$a[x]$表示x点的权值
容易得到方程$f[x]=min(g[x],a[x])$
那么如果点权是动态的呢?
本题中的修改操作只会把点权增加
而真正对答案产生影响的,是某些节点的$f$值取的是$g$值还是$a$值
只有取的值发生改变,$f$值才可能改变,从而对它的祖先节点们产生影响
当我们修改一个点$x$的权值时,$a[x]$会增加,$g[x]$不变,$f[x]$的取值可能会发生改变,要么由取$a$变成取$g$,要么不变,且$f[x]$只可能增加而不会减少
而对于$x$的所有祖先节点的来说,要么由取$g$变成取$a$,要么不变
也就是说,只有我们修改的那个节点,$f$的取值能从$a$变成$g$,且它的$g$值不变
修改操作影响的其他节点,都是从$g$变成$a$,且这些节点的$a$值不变
所以说从$g$变成$a$这种操作,最多出现$n+m$次,这部分我们可以暴力处理
如果我们修改一个节点$x$,可能会有连续的几个祖先会从取$g$变成取$a$,我们暴力修改这些祖先的信息
直到我们碰到了一个祖先,原来是取$g$,修改后还是取$g$,而这样的祖先节点一定是在一条连续的链上的,且它们的数量可能很大,我们称这样的一条链为$T$
考虑树剖+线段树处理这部分祖先的信息,从最下面的点开始,每次拎出来一条重链,先判断$T$的顶端是不是 重链头的某个祖先节点
那么如何判断$T$的顶端是否在重链头的上面呢?
发现如果原来取$g$,现在还取$g$,设修改值是del$T$$a$ (注意这个修改值不一定是修改操作里的那个值!!!而是由链$T$的底端的那个节点的$f$的变化值)
$T$上每一个节点都满足$g[x]+delta \leq a[x]$,即$a[x]-g[x] \geq delta$
我们用线段树维护每个节点的$a[x]-g[x]$值
如果是,暴力修改这部分重链,然后跳掉上面一条重链继续处理
如果不是,说明$T$的顶端在重链内部,前缀/后缀最小值是具有单调性的,二分找到$T$的顶端即可
链$T$顶端的父节点的$f$值只能是由$g$变成$a$,或者不变。如果改变了,就不断重复上述过程即可
复杂度$O(nlog^{2}n)$
代码实现比较复杂
1 #include <vector> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 200010 6 #define ll long long 7 #define dd double 8 #define inf 0x3f3f3f3f3f3f3f3fll 9 using namespace std; 10 11 int gint() 12 { 13 int ret=0,fh=1;char c=getchar(); 14 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 15 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 16 return ret*fh; 17 } 18 void gchar(char *s) 19 { 20 int cnt=0;char c=getchar(); 21 while(c<'A'||c>'Z'){c=getchar();} 22 while(c>='A'&&c<='Z'){s[cnt++]=c;c=getchar();} 23 s[cnt]='\n'; 24 } 25 26 struct Edge{ 27 int head[N1],to[N1<<1],nxt[N1<<1],cte; 28 void ae(int u,int v) 29 {cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte;} 30 }e; 31 32 struct SEG{ 33 ll mi[N1<<2],tag[N1<<2]; 34 inline void pushup(int rt){ mi[rt]=min(mi[rt<<1],mi[rt<<1|1]); } 35 void pushdown(int rt) 36 { 37 if(!tag[rt]) return; 38 mi[rt<<1]+=tag[rt]; mi[rt<<1|1]+=tag[rt]; 39 tag[rt<<1]+=tag[rt]; tag[rt<<1|1]+=tag[rt]; 40 tag[rt]=0; 41 } 42 void build(int *a,ll *g,int *id,int l,int r,int rt) 43 { 44 if(l==r) { mi[rt]=1ll*a[id[l]]-g[id[l]]; return; } 45 int mid=(l+r)>>1; 46 build(a,g,id,l,mid,rt<<1); 47 build(a,g,id,mid+1,r,rt<<1|1); 48 pushup(rt); 49 } 50 void update(int L,int R,int l,int r,int rt,ll w) 51 { 52 if(L<=l&&r<=R){ mi[rt]+=w; tag[rt]+=w; return; } 53 int mid=(l+r)>>1; pushdown(rt); 54 if(L<=mid) update(L,R,l,mid,rt<<1,w); 55 if(R>mid) update(L,R,mid+1,r,rt<<1|1,w); 56 pushup(rt); 57 } 58 ll query(int L,int R,int l,int r,int rt) 59 { 60 if(L<=l&&r<=R) return mi[rt]; 61 int mid=(l+r)>>1; pushdown(rt); ll ans=inf; 62 if(L<=mid) ans=min(ans,query(L,R,l,mid,rt<<1)); 63 if(R>mid) ans=min(ans,query(L,R,mid+1,r,rt<<1|1)); 64 return ans; 65 } 66 }s; 67 68 int n,m; 69 int a[N1]; 70 namespace Split{ 71 int fa[N1],son[N1],tp[N1],sz[N1],dep[N1],st[N1],id[N1],tot; ll g[N1],f[N1]; 72 void dfs1(int u,int dad) 73 { 74 int j,v; sz[u]=1; 75 for(j=e.head[u];j;j=e.nxt[j]) 76 { 77 v=e.to[j]; if(v==dad) continue; 78 dep[v]=dep[u]+1; fa[v]=u; dfs1(v,u); 79 sz[u]+=sz[v]; son[u]=sz[v]>sz[son[u]]?v:son[u]; 80 g[u]+=f[v]; 81 } 82 if(sz[u]>1) f[u]=min(1ll*a[u],g[u]); 83 else f[u]=a[u],g[u]=inf; 84 } 85 void dfs2(int u) 86 { 87 int j,v; st[u]=++tot; id[tot]=u; 88 if(son[u]){ tp[son[u]]=tp[u]; dfs2(son[u]); } 89 for(j=e.head[u];j;j=e.nxt[j]) 90 { 91 v=e.to[j]; if(v==fa[u]||v==son[u]) continue; 92 tp[v]=v; dfs2(v); 93 } 94 } 95 void init() 96 { 97 dfs1(1,-1); tp[1]=1; dfs2(1); 98 s.build(a,g,id,1,n,1); 99 } 100 }; 101 using Split::fa; using Split::st; 102 using Split::id; using Split::tp; 103 104 void update(int x,ll w) 105 { 106 ll gx,dt,gu,mi; int u,flag,l,r,mid,ans; 107 gx=a[x]-s.query(st[x],st[x],1,n,1); 108 s.update(st[x],st[x],1,n,1,w); 109 if(a[x]>=gx){ return; } //x:g->g 110 dt=min(gx-a[x],w); x=fa[x]; 111 while(x){ 112 113 u=x; flag=0; 114 while(u) 115 { 116 gu=a[u]-s.query(st[u],st[u],1,n,1); 117 s.update(st[u],st[u],1,n,1,-dt); 118 if(a[u]<=gu) break; 119 if(gu+dt>a[u]) dt=a[u]-gu; 120 else{ flag=1; u=fa[u]; break; } 121 u=fa[u]; 122 } 123 if(!flag) break; 124 while(u) 125 { 126 mi=s.query(st[tp[u]],st[u],1,n,1); 127 if(mi>=dt){ 128 s.update(st[tp[u]],st[u],1,n,1,-dt); 129 u=fa[tp[u]]; 130 }else{ 131 l=st[tp[u]]; r=st[u]; ans=0; 132 while(l<=r) 133 { 134 mid=(l+r)>>1; 135 if(s.query(mid,st[u],1,n,1)>=dt) ans=id[mid],r=mid-1; 136 else l=mid+1; 137 } 138 if(!ans){ x=u; break; } 139 s.update(st[ans],st[u],1,n,1,-dt); 140 x=fa[ans]; 141 break; 142 } 143 } 144 145 } 146 } 147 ll query(int x){ return min(1ll*a[x],1ll*a[x]-s.query(st[x],st[x],1,n,1)); } 148 149 int main() 150 { 151 scanf("%d",&n); 152 int i,j,k,x,y; char str[10]; 153 for(i=1;i<=n;i++) a[i]=gint(); 154 for(i=1;i<n;i++){ x=gint(); y=gint(); e.ae(x,y); e.ae(y,x); } 155 Split::init(); 156 scanf("%d",&m); 157 for(j=1;j<=m;j++) 158 { 159 gchar(str); 160 if(str[0]=='Q'){ 161 x=gint(); 162 printf("%lld\n",query(x)); 163 }else{ 164 x=gint(); y=gint(); if(!y) continue; 165 update(x,y); a[x]+=y; 166 } 167 } 168 return 0; 169 }