bzoj4712 洪水
题目描述:
给出一棵$n(n<=200000)$的树。每个点有一个权值$w$,表示堵上这个点的花费。
$m$组操作,操作一为将点$x$的权值加上一个非负整数$d$,操作二为询问以点$x$为根的子树内堵上所有根到叶节点路径的最小花费。
题解:
设$h[x]$为$x$的所有子节点的最小花费之和。
一眼看出$dp[i]=min(w[i],h[i])$
由于每次$d$非负,我们可以发现每次只会更新$x$的$w$值,
可能更新一条链上$h$值。
于是树剖维护$dp$。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 200050 #define ll long long inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,hed[N],cnt,m; char ch[2]; struct EG { int to,nxt; }e[2*N]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } int fa[N],son[N],dep[N],siz[N],top[N]; ll h[N],w[N],ff[N]; void dfs1(int u,int f) { fa[u]=f; siz[u]=1; dep[u]=dep[f]+1; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(to==f)continue; dfs1(to,u); siz[u]+=siz[to]; h[u]+=ff[to]; if(siz[to]>siz[son[u]])son[u]=to; } if(siz[u]==1)h[u]=0x3f3f3f3f; ff[u] = min(w[u],h[u]); } int tin[N],tim,pla[N]; void dfs2(int u,int tp) { tin[u]=++tim,pla[tim]=u; top[u] = tp; if(son[u]) { dfs2(son[u],tp); for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(to==fa[u]||to==son[u])continue; dfs2(to,to); } } } struct segtree { ll v1[N<<2],tag[N<<2];//min(w-h) h void add(int u,ll d) { v1[u]-=d; tag[u]+=d; } void pushdown(int u) { if(tag[u]) { add(u<<1,tag[u]); add(u<<1|1,tag[u]); tag[u]=0; } } void update(int u) { v1[u] = min(v1[u<<1],v1[u<<1|1]); } void build(int l,int r,int u) { if(l==r) { v1[u] = w[pla[l]]-h[pla[l]]; tag[u] = h[pla[l]]; return ; } int mid = (l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); update(u); } void insert(int l,int r,int u,int ql,int qr,ll d)//h+=d { if(l==ql&&r==qr) { add(u,d); return ; } pushdown(u); int mid = (l+r)>>1; if(qr<=mid)insert(l,mid,u<<1,ql,qr,d); else if(ql>mid)insert(mid+1,r,u<<1|1,ql,qr,d); else insert(l,mid,u<<1,ql,mid,d),insert(mid+1,r,u<<1|1,mid+1,qr,d); update(u); } void ins(int l,int r,int u,int qx,ll d)//w+=d { if(l==r) { v1[u]+=d; return ; } pushdown(u); int mid = (l+r)>>1; if(qx<=mid)ins(l,mid,u<<1,qx,d); else ins(mid+1,r,u<<1|1,qx,d); update(u); } ll query1(int l,int r,int u,int ql,int qr)//min(w-h) { if(l==ql&&r==qr)return v1[u]; pushdown(u); int mid = (l+r)>>1; if(qr<=mid)return query1(l,mid,u<<1,ql,qr); else if(ql>mid)return query1(mid+1,r,u<<1|1,ql,qr); else return min(query1(l,mid,u<<1,ql,mid),query1(mid+1,r,u<<1|1,mid+1,qr)); } ll query2(int l,int r,int u,int qx)//h { if(l==r)return tag[u]; pushdown(u); int mid = (l+r)>>1; if(qx<=mid)return query2(l,mid,u<<1,qx); else return query2(mid+1,r,u<<1|1,qx); } }tr; int get_lim(int x,ll d) { int l = tin[top[x]],r = tin[x],ans = l-1; while(l<=r) { int mid = (l+r)>>1; if(tr.query1(1,n,1,mid,tin[x])>=d) { r = mid-1; }else { ans = mid; l = mid+1; } } return pla[ans]; } ll F(int x) { return min(w[x],tr.query2(1,n,1,tin[x])); // return min(w[x],h[x]); } int main() { // freopen("1.in","r",stdin); n=rd(); for(int i=1;i<=n;i++)w[i] = rd(); for(int f,t,i=1;i<n;i++) { f = rd(),t = rd(); ae(f,t),ae(t,f); } dfs1(1,0),dfs2(1,1); tr.build(1,n,1); m = rd(); int x,y;ll d,f0,f1; for(int i=1;i<=m;i++) { scanf("%s",ch); if(ch[0]=='Q') { x = rd(); printf("%lld\n",F(x)); }else { x = rd(),d = rd(); /* f0 = F(x); w[x]+=d; f1 = F(x); d = f1-f0; x = fa[x]; while(x&&d) { f0 = F(x); h[x]+=d; f1 = F(x); x = fa[x]; d = f1-f0; }*/ f0 = F(x); w[x]+=d; tr.ins(1,n,1,tin[x],d);//w+=d f1 = F(x); d = f1-f0; x = fa[x]; while(x&&d) { if(tr.query1(1,n,1,tin[top[x]],tin[x])>=d) { tr.insert(1,n,1,tin[top[x]],tin[x],d); x = fa[top[x]]; continue; } y = get_lim(x,d); f0 = F(y); tr.insert(1,n,1,tin[y],tin[x],d); f1 = F(y); x = fa[y],d = f1-f0; } } } return 0; }