BZOJ4712 洪水

41 篇文章 0 订阅
7 篇文章 0 订阅

永恒的一氧化碳大爷在讨论版里发了个单log的做法,还没来得及看……

先写个链剖再说

考虑没有修改的情况,我们可以树DP,f[x]表示把x的子树截断的最小代价,v[x]表示截断每个点的代价,然后我们求出每个点的s[x],代表x的所有儿子节点的f之和,f[x]就等于min(v[x],s[x])

考虑修改,一次修改操作后,v[x]会增加,f[x]可能增加,可能不变,如果f[x]增加了d,那么从x到根的一段路径上的点的s值和f值都会增加d,直到第一个有v[x]-s[x]<=d的点,即s[x]在加d之后会变得比v[x]大,或原来v[x]就小于等于s[x],则f[x]不再等于s[x]+d,而改为v[x],则增加量会改变一次,我们继续对顶上的路径重复上述过程,直到增加量为0或者到根

对于一次处理增加操作,我们可以用链剖加线段树,支持区间加和区间查询v[x]-s[x]的最小值,在线段树上爬来找到第一个v[x]-s[x]<=d的点

由于修改操作的增加量是非负的,并且每次只更改一个点,所以至多有n+m次s[x]变得比v[x]大的时刻,处理一个这样的时刻是O(log^2 n)的,所以总复杂度O((n+m) log^2 n)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 200010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
struct vec{
	ll to;
	ll fro;
};
vec mp[MAXN*2];
ll tai[MAXN],cnt;
ll n,m;
ll v[MAXN],s[MAXN],f[MAXN];
ll fa[MAXN],son[MAXN],tp[MAXN],siz[MAXN],dfn[MAXN],ndf[MAXN],tim;
ll mn[MAXN<<2],ch[MAXN<<2];
inline void be(ll x,ll y){
	mp[++cnt].to=y;
	mp[cnt].fro=tai[x];
	tai[x]=cnt;
}
inline void bde(ll x,ll y){
	be(x,y);
	be(y,x);
}
void dfs(ll x){
	ll i,y;
	siz[x]=1;
	bool flag=0;
	s[x]=INF;
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(!siz[y]){
			fa[y]=x;
			dfs(y);
			if(!flag){
				flag=1;
				s[x]=0;
			}
			s[x]+=f[y];
			siz[x]+=siz[y];
			if(siz[son[x]]<siz[y]){
				son[x]=y;
			}
		}
	}
	f[x]=min(v[x],s[x]);
}
void dfs2(ll x,ll z){
	ll i,y;
	ndf[dfn[x]=++tim]=x;
	tp[x]=z;
	if(son[x]){
		dfs2(son[x],z);
		for(i=tai[x];i;i=mp[i].fro){
			y=mp[i].to;
			if(!dfn[y]){
				dfs2(y,y);
			}
		}
	}
}
inline void ud(ll x){
	mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
inline void toch(ll x,ll y){
	ch[x]+=y;
	mn[x]-=y;
}
inline void pd(ll x){
	if(ch[x]){
		toch(x<<1,ch[x]);
		toch(x<<1|1,ch[x]);
		ch[x]=0;
	}
}
void build(ll x,ll y,ll z){
	if(y==z){
		mn[x]=v[ndf[y]]-s[ndf[y]];
		return ;
	}
	ll mid=y+z>>1;
	build(x<<1,y,mid);
	build(x<<1|1,mid+1,z);
	ud(x);
}
ll change(ll x,ll y,ll z,ll l,ll r,ll cv){
	if(y==z){
		toch(x,cv);
		if(mn[x]+cv<=cv){
			return ndf[y];
		}else{
			return 0;
		}
	}
	pd(x);
	ll t=0;
	ll mid=y+z>>1;
	if(y==l&&z==r){
		if(mn[x]>cv){
			toch(x,cv);
			return 0;
		}
	}
	if(r<=mid){
		t=change(x<<1,y,mid,l,r,cv);
	}else if(l>mid){
		t=change(x<<1|1,mid+1,z,l,r,cv);
	}else{
		t=change(x<<1|1,mid+1,z,mid+1,r,cv);
		if(!t){
			t=change(x<<1,y,mid,l,mid,cv);
		}
	}
	ud(x);
	return t;
}
ll ask(ll x,ll y,ll z,ll p){
	if(y==z){
		return mn[x];
	}
	pd(x);
	ll mid=y+z>>1;
	if(p<=mid){
		return ask(x<<1,y,mid,p);
	}else{
		return ask(x<<1|1,mid+1,z,p);
	}
}
void tochange(ll x,ll y){
	if(y<=0||!x){
		return ;
	}
	while(x){
		ll t=change(1,1,n,dfn[tp[x]],dfn[x],y);
		if(!t){
			x=fa[tp[x]];
		}else{
			tochange(fa[t],ask(1,1,n,dfn[t])+y);
			return ;
		}
	}
}
int main(){
	ll i,x,y;
	char o[2];
	scanf("%lld",&n);
	for(i=1;i<=n;i++){
		scanf("%lld",&v[i]);
	}
	for(i=1;i<n;i++){
		scanf("%lld%lld",&x,&y);
		bde(x,y);
	}
	dfs(1);
	dfs2(1,1);
	build(1,1,n);
	scanf("%lld",&m);
	while(m--){
		scanf("%s",o);
		if(o[0]=='Q'){
			scanf("%lld",&x);
			ll s=v[x]-ask(1,1,n,dfn[x]);
			printf("%lld\n",min(s,v[x]));
		}
		if(o[0]=='C'){
			scanf("%lld%lld",&x,&y);
			if(!y){
				continue ;
			}
			v[x]+=y;
			change(1,1,n,dfn[x],dfn[x],-y);
			ll s=v[x]-ask(1,1,n,dfn[x]);
			f[x]=min(v[x],s);
			tochange(fa[x],f[x]-v[x]+y);
		}
	}
	return 0;
}

/*
5
5 7 4 1 5 
2 1
3 2
4 3
5 3
5
C 3 10
C 1 6
Q 1




*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值