[BZOJ4712]洪水-[树链剖分+线段树]
Description
小A走到一个山脚下,准备给自己造一个小屋。这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小A面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个n个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。
Solution
这道题真的是666。。
我们设g[x]为堵住该点所有子树的和,v[x]为堵住该点的代价,则f[x]=min(g[x],v[x])。现在我们要给v[x]加上to。
1,v[x]>=g[x],v[x]加多少都不会有影响,过;
2,v[x]<=g[x]&&v[x]+to<=g[x],则g[fa[x]]+=to,如果f[fa[x]]改变,则还需要接着往上推。
3,v[x]<=g[x]&&v[x]+to>g[x],则g[fa[x]]+=-v[x]+g[x],如果f[fa[x]]改变,同样需要接着往上推。
对于情况1,直接处理掉就ok了。
我们考虑优化情况2和3的处理。在这里我们采用树剖+线段树。线段树存储v[x]-g[x]。
对于点x,我们沿着重链往上,如果在某条重链上都是情况2或3,直接加就好;反之在这条重链上二分。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long ll; int n,x,y; ll v[200010],_g[200010],dp[200010],d; struct node{int y,nxt; }g[400010];int h[200010],tot=0; int m;char ch[5]; int fa[200010],dep[200010],son[200010],top[200010],sz[200010]; int cnt=0,dfn[200010],id[200010]; struct XD_TREE { ll mn[800010],tag[800010]; void build(int k,int l,int r) { if (l==r) {mn[k]=v[dfn[l]]-_g[dfn[l]];return;} int mid=(l+r)/2; build(k<<1,l,mid);build(k<<1|1,mid+1,r); mn[k]=min(mn[k<<1],mn[k<<1|1]); } void downtag(int k) { mn[k<<1]-=tag[k];mn[k<<1|1]-=tag[k]; tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k]; tag[k]=0; } int modify(int k,int l,int r,int askl,int askr,ll d) { if (l==r){ mn[k]-=d;tag[k]+=d; if (mn[k]<=0) return dfn[l]; return 0; } if (askl<=l&&r<=askr){ if (mn[k]>d) { mn[k]-=d;tag[k]+=d;return 0; } } if (tag[k]!=0) downtag(k); int mid=(l+r)/2,re=0; if (askr>mid) re=modify(k<<1|1,mid+1,r,askl,askr,d); if (askl<=mid&&!re) re=modify(k<<1,l,mid,askl,askr,d); mn[k]=min(mn[k<<1],mn[k<<1|1]); return re; } ll query(int k,int l,int r,int ask) { if (l==r) return mn[k]; if (tag[k]!=0) downtag(k); int mid=(l+r)/2; if (ask<=mid) return query(k<<1,l,mid,ask); else return query(k<<1|1,mid+1,r,ask); } }X; struct TREE_LINK//树剖 { void dfs1(int x,int f) { fa[x]=f;dep[x]=dep[f]+1;sz[x]=1; for(int i=h[x];i;i=g[i].nxt) if (g[i].y!=f) { dfs1(g[i].y,x); sz[x]+=sz[g[i].y]; son[x]=(sz[son[x]]>=sz[g[i].y])?son[x]:g[i].y; _g[x]+=dp[g[i].y]; } if (sz[x]>1) dp[x]=min(_g[x],v[x]);else dp[x]=v[x],_g[x]=1e9; } void dfs2(int x) { dfn[++cnt]=x;id[x]=cnt; top[x]=son[fa[x]]==x?top[fa[x]]:x; if (son[x]) dfs2(son[x]); for (int i=h[x];i;i=g[i].nxt) if (g[i].y!=fa[x]&&g[i].y!=son[x]) dfs2(g[i].y); } void work(int x,ll d) { if (!x||d<=0) return; while (x) { int t=X.modify(1,1,n,id[top[x]],id[x],d); if (t) {work(fa[t],X.query(1,1,n,id[t])+d);break;} x=fa[top[x]]; } } }T; ll getg(int x){return v[x]-X.query(1,1,n,id[x]);} int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&v[i]); for (int i=1;i<n;i++) { scanf("%d%d",&x,&y); g[++tot]=node{y,h[x]};h[x]=tot; g[++tot]=node{x,h[y]};h[y]=tot; } T.dfs1(1,0); T.dfs2(1); X.build(1,1,n); scanf("%d",&m); for (int i=1;i<=m;i++) { scanf("%s",ch); if (ch[0]=='Q') { scanf("%d",&x); ll gg=getg(x); printf("%lld\n",min(gg,v[x])); } else { scanf("%d%lld",&x,&d); v[x]+=d; X.modify(1,1,n,id[x],id[x],-d); ll gg=getg(x); if (v[x]-d>=gg) continue; if (v[x]<=gg) T.work(fa[x],d); else T.work(fa[x],gg-v[x]+d); } } }