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 }

 

posted @ 2019-01-14 16:20  guapisolo  阅读(188)  评论(0编辑  收藏  举报