zoukankan      html  css  js  c++  java
  • 【BZOJ4712】洪水 树链剖分优化DP+线段树

    【BZOJ4712】洪水

    Description

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

    Input

     输入文件第一行包含一个数n,表示树的大小。

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

    Output

     对于每次询问操作,输出对应的答案,答案之间用换行隔开。

    Sample Input

    4
    4 3 2 1
    1 2
    1 3
    4 2
    4
    Q 1
    Q 2
    C 4 10
    Q 1

    Sample Output

    3
    1
    4

    题解:设每个点的DP值是f[x],每个点儿子的f之和为g[x],f[x]=min(g[x],v[x])。发现,由于v[x]只能变大,所以每次操作可能导致一些点从选g[x]变成选v[x],但只能导致一个点从选v[x]变成选g[x],所以对于从选v变成选g或从选g变成选v的,我们都可以暴力搞定(均摊的思想)。具体做法:

    先改v[x],如果v[x]的增长导致了f[x]的改变,则令它父亲的g值+=改变值,然后递归它的父亲:

    先让当前的g值+=该变量,如果f不变,则结束;如果原来选g,改完变成选v,那么记录该变量,继续修改它的父亲;如果原来选g,改完还是选g,那么一定是:当前点的连续几辈祖先都是原来选g改完也选g,那么我们二分找到深度最小的祖先,然后中间的g值都能用线段树直接修改。然后再继续修改这个祖先的父亲即可。

    那么二分的过程具体如何实现呢?需要用线段树维护v[x]-g[x]的最小值,然后再树剖的时候,先判断当前链的链顶是否符合要求,如果不是,则在链内二分,否则在直接跳到下一条链上。

    这样就保证了时间复杂度是$O(nlog_n^2)$的。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    const int maxn=200010;
    typedef long long ll;
    int n,m,cnt;
    int to[maxn<<1],next[maxn<<1],head[maxn],p[maxn],Q[maxn],dep[maxn],fa[maxn],top[maxn],siz[maxn],son[maxn];
    char str[5];
    ll sv[maxn<<2],ts[maxn<<2],sc[maxn<<2],v[maxn],g[maxn];
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void dfs1(int x)
    {
    	siz[x]=1;
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
    	{
    		fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]];
    		if(siz[to[i]]>siz[son[x]])	son[x]=to[i];
    		g[x]+=min(g[to[i]],v[to[i]]);
    	}
    	if(siz[x]==1)	g[x]=1<<30;
    }
    void dfs2(int x,int tp)
    {
    	p[x]=++p[0],Q[p[0]]=x,top[x]=tp;
    	if(son[x])	dfs2(son[x],tp);
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x]&&to[i]!=son[x])	dfs2(to[i],to[i]);
    }
    inline void pushdown(int x)
    {
    	if(ts[x])	sc[lson]-=ts[x],sc[rson]-=ts[x],ts[lson]+=ts[x],ts[rson]+=ts[x],ts[x]=0;
    }
    void build(int l,int r,int x)
    {
    	if(l==r)
    	{
    		sv[x]=v[Q[l]],sc[x]=v[Q[l]]-g[Q[l]];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,lson),build(mid+1,r,rson);
    	sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]);
    }
    ll getg(int l,int r,int x,int a)
    {
    	if(l==r)	return sv[x]-sc[x];
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(a<=mid)	return getg(l,mid,lson,a);
    	return getg(mid+1,r,rson,a);
    }
    ll getv(int l,int r,int x,int a,int b)
    {
    	if(l==r)	return sv[x];
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(b<=mid)	return getv(l,mid,lson,a,b);
    	if(a>mid)	return getv(mid+1,r,rson,a,b);
    	return min(getv(l,mid,lson,a,b),getv(mid+1,r,rson,a,b));
    }
    ll getc(int l,int r,int x,int a,int b)
    {
    	if(a<=l&&r<=b)	return sc[x];
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(b<=mid)	return getc(l,mid,lson,a,b);
    	if(a>mid)	return getc(mid+1,r,rson,a,b);
    	return min(getc(l,mid,lson,a,b),getc(mid+1,r,rson,a,b));
    }
    void upg(int l,int r,int x,int a,int b,ll c)
    {
    	if(a<=l&&r<=b)
    	{
    		if(c==-1)	sc[x]+=v[Q[l]]-sv[x],sv[x]=v[Q[l]];
    		else	sc[x]-=c,ts[x]+=c;
    		return ;
    	}
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(a<=mid)	upg(l,mid,lson,a,b,c);
    	if(b>mid)	upg(mid+1,r,rson,a,b,c);
    	sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]);
    }
    inline void change(int x,ll d)
    {
    	while(x)
    	{
    		ll gx=getg(1,n,1,p[x]),fx=min(gx,v[x]);
    		upg(1,n,1,p[x],p[x],d);
    		if(gx>=v[x])	return ;
    		if(gx+d>v[x])	d=v[x]-fx,x=fa[x];
    		else
    		{
    			if(top[x]==x)	x=fa[x];
    			else	if(getc(1,n,1,p[top[x]],p[x]-1)>=d)
    			{
    				upg(1,n,1,p[top[x]],p[x]-1,d);
    				x=fa[top[x]];
    			}
    			else
    			{
    				int l=p[top[x]],r=p[x],mid;
    				while(l<r)
    				{
    					mid=(l+r)>>1;
    					if(getc(1,n,1,mid,p[x]-1)>=d)	r=mid;
    					else	l=mid+1;
    				}
    				if(r<p[x])	upg(1,n,1,r,p[x]-1,d);
    				x=Q[r-1];
    			}
    		}
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd();
    	int i,a,b;
    	for(i=1;i<=n;i++)	v[i]=rd();
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	dep[1]=1,dfs1(1),dfs2(1,1);
    	m=rd();
    	build(1,n,1);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%s",str);
    		if(str[0]=='C')
    		{
    			a=rd(),b=rd();
    			ll gx=getg(1,n,1,p[a]),fx=min(gx,v[a]);
    			v[a]+=b,upg(1,n,1,p[a],p[a],-1);
    			if(min(v[a],gx)>fx)	change(fa[a],min(v[a],gx)-fx);
    		}
    		else	a=rd(),printf("%lld
    ",min(v[a],getg(1,n,1,p[a])));
    	}
    	return 0;
    }//4 4 3 2 1 1 2 1 3 4 2 4 Q 1 Q 2 C 4 10 Q 1

     

  • 相关阅读:
    ElementUi
    Vue插件
    Vue-cli
    Vue进阶
    Vue组件
    Vue生命期钩子
    Vue基础
    Vue介绍
    logging模块
    time模块
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7629904.html
Copyright © 2011-2022 走看看