zoukankan      html  css  js  c++  java
  • 【BZOJ4712】洪水

    Description

      
      ​ 原题链接(权限题)
      
    ​   题目大意:给一棵带点权的树,要求支持两种操作:
      
    ​   (1)给某一个点的权值加上(x)
      
    ​   (2)令堵上一个点的代价为其权值,询问以某一个点(u)为根的子树中,至少花费多少代价,使得(u)与子树中的叶子节点不连通(不算(u),可以赌叶子节点和(u)本身)
      
    ​   点数(n le 200000)
      
      
      

    Solution

      
    ​   静态DP很好做:记(u)的点权为(w_u),设(f_u)表示(u)子树的答案,则有

    [f_u=min{w_u,sum_vf_v} ]

    ​   现在要支持点权修改,怎么实现?这种题貌似叫动态DP,可以用树剖的套路来解决。
      
    ​   令(son_u)表示(u)的重儿子,我们把重儿子的权值分离出来:

    [f_u=min{w_u,f_{son_u}+sum_{v e son_u}f_v} ]

    ​   令后面一个和式为(g_u),则有

    [f_u=min{w_u,g_u+f_{son_u}} ]

    ​   使用树链剖分维护两个值:每个点(u)(w_u)(g_u)
      
    ​   接下来,在此表示法的意义下,我们考虑询问(u)时,其答案(f_u)具体怎么体现。根据套路,我们要尽可能用一种线段树能维护的问题形式表现答案。我们发现,(f_u)所含的比较的元素可以展开来看(其中(v_i)表示(u)(i)级重儿子):

    [min{w_u,g_u+w_{v_1},g_u+g_{v_1}+w_{v_2},g_u+g_{v_1}+g_{v_2}+w_{v_3},...} ]

      ​ 这其实就是一个最小前缀和的形式,只不过前缀和的最后一个元素是该位置的(w),其余元素是(g)。这其实很好实现,用线段树来维护每一条重链的最小前缀和。具体实现上,两两重链之间插入一个特殊点,其权值设为正无穷,以限制考虑的前缀不可在链之间跨越。
      
    ​   所以,(w)(g)是真正记录在树剖中的数据,而每个点(u)(f_u),则是体现为从(u)点走重链向下的最小前缀和。
      
    ​   修改上,也是这类问题的一贯做法。首先单独修改(u)的权值(w_u)。根据定义,其他点的(w)一定不会受到影响。当下唯一影响的,就是(u)所在链链顶元素的(g)值。我们计算出于(u)修改(w_u)后与修改前,链顶(f)值的变化量。这个变化量只会影响到链顶的父亲——另一条重链上的某一个点的(g)值,在线段树上修改。我们发现,这时候我们的操作模式已经和“单独修改(u)的权值(w_u)”一模一样了,只不过初始时我们修改(w_u)通过线段树上推的方式间接影响全局,而现在我们通过直接修改(g_u)的方式直接影响全局。因此计算出该点修改前后,其链顶的(f)的差值,继续向上重复这一过程即可。
      
    ​   可以发现,这一过程发生的次数,就是从初始点往根节点走遇到的轻边数量。由于每一个点到根节点经过的轻链条数为(mathcal O(log n)),因此单次修改的复杂度是(mathcal O(log ^2 n))
      
      
      
    ​   在做这题之前,我已经做过了类似的一道动态DP题,并且很快地想到了将重儿子的数据单独分离的套路。可是我在探究答案究竟怎么体现的时候卡住了,没有发现“最小前缀和”的形式,所以没有写出来。
      
      
      

    Code

      

    #include <cstdio>
    using namespace std;
    typedef long long ll;
    const int N=400005;
    const ll INF=1LL<<30;
    int n;
    ll w[N],f[N],g[N];
    int h[N],tot;
    struct Edge{
    	int v,next;
    }e[N*2];
    int pre[N],dep[N],size[N],son[N],who[N],loc[N],locnt,top[N];
    inline ll min(ll x,ll y){
    	return x<y?x:y;
    }
    inline void addEdge(int u,int v){
    	e[++tot]=(Edge){v,h[u]}; h[u]=tot;
    	e[++tot]=(Edge){u,h[v]}; h[v]=tot;
    }
    void readData(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%lld",w+i);
    	int u,v;
    	for(int i=1;i<n;i++){
    		scanf("%d%d",&u,&v);
    		addEdge(u,v);
    	}
    }
    void d_dfs1(int u,int fa){
    	pre[u]=fa;
    	dep[u]=dep[fa]+1;
    	son[u]=0;
    	size[u]=1;
    	g[u]=0;
    	bool have=false;
    	for(int i=h[u],v;i;i=e[i].next)
    		if((v=e[i].v)!=fa){
    			have=true;
    			d_dfs1(v,u);
    			size[u]+=size[v];
    			if(!son[u]||size[v]>size[son[u]])
    				son[u]=v;
    			g[u]+=f[v];
    		}
    	if(!have) 
    		f[u]=w[u], g[u]=INF;
    	else 
    		f[u]=min(w[u],g[u]);
    }
    void d_dfs2(int u,int _top){
    	top[u]=_top;
    	loc[u]=++locnt;
    	who[locnt]=u;
    	if(!son[u]){
    		who[++locnt]=0;
    		return;
    	}
    	g[u]-=f[son[u]];
    	d_dfs2(son[u],_top);
    	for(int i=h[u],v;i;i=e[i].next)
    		if((v=e[i].v)!=pre[u]&&v!=son[u])
    			d_dfs2(v,v);
    }
    namespace SEG{/*{{{*/
    	const int S=N*2;
    	int rt,sz;
    	int ch[S][2];
    	ll sum[S],best[S];
    	void pushup(int u){
    		sum[u]=sum[ch[u][0]]+sum[ch[u][1]];
    		best[u]=min(best[ch[u][0]],sum[ch[u][0]]+best[ch[u][1]]);
    	}
    	void build(int &u,int l,int r){
    		u=++sz;
    		if(l==r){
    			int x=who[l];
    			sum[u]=g[x];
    			best[u]=w[x];
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(ch[u][0],l,mid);
    		build(ch[u][1],mid+1,r);
    		pushup(u);
    	}
    	void modify(int u,int l,int r,int pos,ll delta){
    		if(l==r){
    			int x=who[l];
    			best[u]=w[x];
    			sum[u]+=delta;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(pos<=mid)
    			modify(ch[u][0],l,mid,pos,delta);
    		else
    			modify(ch[u][1],mid+1,r,pos,delta);
    		pushup(u);
    	}
    	ll query(int u,int l,int r,int L,ll &lsum){
    		if(L<=l){
    			ll res=lsum+best[u];
    			lsum+=sum[u];
    			return res;
    		}
    		int mid=(l+r)>>1;
    		ll res=INF;
    		if(L<=mid) 
    			res=min(res,query(ch[u][0],l,mid,L,lsum));
    		res=min(res,query(ch[u][1],mid+1,r,L,lsum));
    		return res;
    	}
    	ll queryBest(int l){
    		ll lsum=0;
    		return query(rt,1,locnt,l,lsum);
    	}
    }/*}}}*/
    void initTree(){
    	d_dfs1(1,0);
    	d_dfs2(1,1);
    	w[0]=INF;
    	SEG::build(SEG::rt,1,locnt);
    }
    void modify(int u,ll x){
    	ll delta=0,oldval,newval;
    	w[u]+=x;
    	for(;u;u=pre[top[u]]){
    		oldval=SEG::queryBest(loc[top[u]]);
    		SEG::modify(SEG::rt,1,locnt,loc[u],delta);
    		newval=SEG::queryBest(loc[top[u]]);
    		delta=newval-oldval;
    		if(!delta) break;
    	}
    }
    void answerQuery(){
    	int q;
    	char opt[2];
    	ll x,y;
    	scanf("%d",&q);
    	while(q--){
    		scanf("%s%lld",opt,&x);
    		if(opt[0]=='Q')
    			printf("%lld
    ",SEG::queryBest(loc[x]));
    		else{
    			scanf("%lld",&y);
    			modify(x,y);
    		}
    	}
    }
    int main(){
    	readData();
    	initTree();
    	answerQuery();
    	return 0;
    }
    

  • 相关阅读:
    【杭电】[2092]整数解cpp
    【杭电】[2045]不容易系列之(3)——LELE的RPG难题
    【杭电】[2045]不容易系列之(3)——LELE的RPG难题
    【杭电】[2502]月之数
    【杭电】[2502]月之数
    【杭电】[2187]悼念512汶川大地震遇难同胞——老人是真饿了
    【杭电】[2187]悼念512汶川大地震遇难同胞——老人是真饿了
    【杭电】[1877]又一版 A+B
    【杭电】[1877]又一版 A+B
    Sevlet 02: Servlet对比JSP
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9513724.html
Copyright © 2011-2022 走看看