• BZOJ4712 洪水


    Description

    小A走到一个山脚下,准备给自己造一个小屋。这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小A面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个$n$个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

    Input

    输入文件第一行包含一个数$n$,表示树的大小,其中$1$号点是根。

    接下来一行包含$n$个数,表示第$i$个点的权值。
    接下来$n-1$行每行包含两个数$fr$,$to$。表示树中有一条边$(fr,to)$。
    接下来一行一个整数,表示操作的个数。
    接下来$m$行每行表示一个操作,若该行第一个字符为Q,则表示询问操作,后面跟一个参数$x$,表示对应子树的根;若为C,则表示修改操作,后面接两个参数$x$,$to$,表示将点$x$的权值加上$to$。
    $n,mleq 200000$,保证任意$to$都为非负数

    Output

     对于每次询问操作,输出对应的答案,答案之间用换行隔开。
     
    题目大意
    有一棵树,给每一个点涂色都有一个代价,求最小的代价使得每一个点的到根的路径上都至少有一个点被染色,有修改点的代价、询问子树答案两种操作,其中修改点的代价只会增加。
     
    如果不加修改就是一个简单的的$Dp$,加上修改也可以从转移方程入手。
    设$w[x]$表示给$x$点染色的代价。
    设$F[x]$表示使得以$x$为根的子树符合要求的最小染色代价。
    设$G[x]$表示所有$x$的儿子节点$k$的$F[k]$之和。
    特别的,对于叶子节点$t$,$F[t]=w[t],G[t]=INF$。
    所以有$F[x]=min(w[x],G[x])$。
    $w[x]$可以$O(1)$修改,难点在于动态维护$G[x]$的值。
     
    考虑维护$C[x]=w[x]-G[x]$,当我们增加了$x$的代价$d$时$$,影响的只有从$x$到根路径上的点。
    其中一定会有一段与$x$相邻的路径,其中每一个点$k$都满足$C[k]geq d$,这部分的点满足$G[k]+dleq w[k]$,我们只需要将这些点的$C[k]$减去$d$即可,表示这些点的$F$值增加了$d$,假定这是情况一。
    然后就是出现了第一个$C[k]<d$的点,表示通过这次增加代价,$G[k]$超过了$w[k]$,这时$F[k]$的增量就是$C[k]$,而$C[k]$增加的值仍是$d$,再递归处理即可,视为情况二。
    注意当递归时增量为负,表示$w[x]<G[x]$,增加了$G[x]$的值对$x$以及$x$的祖先无影响,就不再继续修改,看作情况三。
     
    这样的复杂度会不会炸掉呢?答案显然是否定的。修改时,若未出现情况二,则没有递归处理,根据重链剖分,复杂度为$O(log^2n)$。否则,若出现了情况二,则下次再处理这个点时就会以情况三出现,也就是每个点最多贡献一次递归的层数。当然,增加一个点的$w[x]$会使这个点再度有可能贡献一层,所以递归的层数不超过$n+m$,每层的复杂度为$O(log^2n)$,复杂度为$O((n+m)log^2n)$。
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define INF 1000000000000000ll
    #define mid ((l+r)>>1)
    #define LL long long
    #define M 500020
    using namespace std;
    LL read(){
    	LL nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    void write(LL x){if(x>9) write(x/10); putchar(x%10+'0');}
    LL n,m,fs[M],nt[M<<1],to[M<<1],tp[M],fa[M],tmp;
    void link(LL x,LL y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
    LL cnt,dfn[M],tk[M],u,v,mxs[M],sz[M],dep[M];
    LL w[M],f[M],g[M],rm[M<<2],tg[M<<2];
    char ch[20];
    void dfs1(LL x,LL last){
    	f[x]=w[x],sz[x]=1,fa[x]=last;
    	if(nt[fs[x]]==-1&&x!=1){g[x]=INF;return;}
    	for(LL i=fs[x];i!=-1;i=nt[i]){
    		if(to[i]==last) continue;
    		dfs1(to[i],x),sz[x]+=sz[to[i]],g[x]+=f[to[i]];
    		if(sz[to[i]]>sz[mxs[x]]) mxs[x]=to[i];
    	}
    	f[x]=min(f[x],g[x]);
    }
    void dfs2(LL x,LL dtp){
    	tp[x]=dtp,dfn[x]=++cnt,tk[cnt]=x;
    	if(mxs[x]) dfs2(mxs[x],dtp);
    	for(LL i=fs[x];i!=-1;i=nt[i]) if(!dfn[to[i]]) dfs2(to[i],to[i]);
    }
    void pushdown(LL x){
    	rm[x<<1]-=tg[x],rm[x<<1|1]-=tg[x];
    	tg[x<<1]+=tg[x],tg[x<<1|1]+=tg[x],tg[x]=0;
    }
    void pushup(LL x){rm[x]=min(rm[x<<1],rm[x<<1|1]);}
    void build(LL x,LL l,LL r){
    	if(l==r){rm[x]=w[tk[l]]-g[tk[l]];return;}
    	build(x<<1,l,mid),build(x<<1|1,mid+1,r),pushup(x);
    }
    LL upd(LL x,LL l,LL r,LL L,LL R,LL dt){
    	if(l==r){rm[x]-=dt,tg[x]+=dt;return rm[x]<=0?tk[l]:0ll;}
    	if(L<=l&&r<=R&&rm[x]>=dt){rm[x]-=dt,tg[x]+=dt;return 0;}
    	pushdown(x); LL now=0;
    	if(R>mid) now=upd(x<<1|1,mid+1,r,L,R,dt);
    	if(L<=mid&&!now) now=upd(x<<1,l,mid,L,R,dt);
    	pushup(x); return now;
    }
    LL getnum(LL x,LL l,LL r,LL pos){
    	if(l==r) return rm[x]; pushdown(x);
    	if(pos<=mid) return getnum(x<<1,l,mid,pos);
    	else return getnum(x<<1|1,mid+1,r,pos);
    }
    void solve(LL x,LL dt){
    	if(!x||dt<=0) return;
    	for(;x;x=fa[tp[x]]){
    		LL now=upd(1,1,n,dfn[tp[x]],dfn[x],dt);
    		if(now) return solve(fa[now],getnum(1,1,n,dfn[now])+dt);
    	}
    }
    int main(){
    	n=read(),memset(fs,-1,sizeof(fs));
    	for(LL i=1;i<=n;i++) w[i]=read();
    	for(LL i=1;i<n;i++) u=read(),v=read(),link(u,v),link(v,u);
    	dfs1(1,0),dfs2(1,1),build(1,1,n);
    	for(LL T=read();T;T--){
    		scanf("%s",ch),u=read();
    		if(ch[0]!='Q'){
    		    m=read(),w[u]+=m,upd(1,1,n,dfn[u],dfn[u],-m);
    			solve(fa[u],min(w[u],w[u]-getnum(1,1,n,dfn[u]))-w[u]+m);
    		}
    		else m=min(w[u],w[u]-getnum(1,1,n,dfn[u])),write(m),putchar('
    ');
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    layui弹出层:使用icon图标小结
    layui弹出层:使用icon图标小结
    存储过程分页
    .net 更改GridView标题文字
    PHP文件结尾符的问题
    JAVA中使用JSON进行数据传递
    Ubuntu LAMP环境安装
    PHP中使用class_exists判断类是否存在
    NS_ERROR_XPC_BAD_CONVERT_JS: Could not convert JavaScript argument
    Android开发教程 葵花宝典第六层 控件之 Dialog ListView GridView
  • 原文地址:https://www.cnblogs.com/OYJason/p/9641650.html
Copyright © 2020-2023  润新知