题解:P11189 「KDOI-10」水杯降温

Link\text{Link}

题意

给出一棵 nn 个节点的树,根节点为 11,节点 ii 有一个点权 aia_i。你可以对这棵树进行如下操作:

  1. 将节点 ii 的子树内的所有点点权增加 11
  2. 选择一个叶子 ii,将 ii 到根节点的链上所有点的点权减少 11

求是否能进行若干次操作,使整个树的点权全为 00

分析

点权全为 00 等价于根节点点权为 00,且任意一个非根节点与其父节点的差为 00;再结合操作修改时同时被修改的点之间的差不变,可以考虑从差分的角度分析操作。

bi=afaiai(2in)b_i=a_{fa_i}-a_i(2\le i\le n),那么一次操作 11 相当于将 bib_i11,若 ii11 则使 a1+1a_1+1,操作 22 相当于将链上所有点的不在链上的儿子的 bib_i11,同时使 a1a_111。也就是说差分数组只会减小!由于最后 bi=0b_i=0,所以若开始时存在 bi<0b_i<0,则无解。

在满足 bi0b_i\ge 0 的情况下:

  1. a10a_1\le 0,那么我们完全可以只用 11 操作把 a1a_1 增加到 00,再用操作 11 把所有的 bib_i 变为 00 即可。
  2. a1>0a_1>0,则必须用若干 22 操作将 a1a_1 减小到非正数,同时保证 bib_i 非负。

考虑到交换操作顺序不影响答案,我们钦定所有 22 操作在 11 操作之前,这样问题就变成能否通过若干次操作 22 使树满足情况 11 的前置条件。

由于有 bib_i 的限制,我们能进行的操作 22 的次数必然有上限,于是我们考虑求出这个上限 mm,判断是否 a1ma_1\le m 即可,

fif_i 表示节点 ii 的子树内能进行的操作 22 的最大次数,若 ii 为叶节点,显然 fi=+f_i=+\infty,否则,二分出 fif_i。具体地,二分答案为 valval,对于每个儿子 jj,我们先执行 dj=min{fj,max{valbj,0}}d_j=\min\{f_j,\max\{val-b_j,0\}\}22 操作,即在保证 bj(valdj)0b_j-(val-d_j)\ge 0 的情况下执行尽可能少的操作,valdjval-d_j 是其他儿子执行操作对 bjb_j 的影响,若某个 bjb_j 执行后不满足条件,或总执行次数超过了 valval,则无解。否则,我们分配剩下的操作,随便地给 fj>djf_j> d_jjj 分配次数,能分配完则有解。这样的贪心策略正确性显然。

最终时间复杂度 O(nlogV)O(n\log V)VV 为值域。

代码

考场代码,有一些无用的片段,请自动忽略。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch))
	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=1e5+10;
int C,T,n;
int head[N],ver[N<<1],nxt[N<<1],tot,fa[N];
void add(int x,int y){
	ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
ll a[N],f[N],b[N],d[N];
int id[N];
bool check(int ti,ll val,int x){
	ll v=val;
	for(int i=1;i<=ti;i++){
		b[i]=a[x]-a[id[i]];
		d[i]=min(f[id[i]],max(val-b[i],0ll));
		if(b[i]+d[i]<val)return 0;
		v-=d[i];b[i]+=d[i];
		if(v<0)return 0;
	}
	for(int i=1;i<=ti;i++){
		ll e=min(f[id[i]]-d[i],v);
		v-=e;
	}
	if(v!=0)return 0;
	return 1;
}
void dfs(int x){
	for(int i=head[x];i;i=nxt[i]){
		int y=ver[i];
		dfs(y);
	}
	int ti=0;
	for(int i=head[x];i;i=nxt[i])
		id[++ti]=ver[i];
	if(!ti)f[x]=2e12+1;
	else if(ti==1){
		f[x]=f[id[ti]];
	}
	else{
		ll l=0,r=0;
		//printf("x=%d\nid:",x);
		for(int i=1;i<=ti;i++){
			//printf("%d ",id[i]);
			r+=f[id[i]];
			r=min(r,(ll)(2e12+1));
		}
		while(l<r){
			ll mid=(l+r+1)>>1;
			if(check(ti,mid,x)){
				l=mid;
			}
			else r=mid-1;
		}
		f[x]=l;
	}
	//printf("f[%d]=%lld\n",x,f[x]);
}
void solve(){
	for(int i=2;i<=n;i++)
		if(a[fa[i]]<a[i]){
			puts("Shuiniao");return;
		}
	dfs(1);
	if(a[1]-f[1]<=0)puts("Huoyu");
	else puts("Shuiniao");
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	C=read();T=read();
	while(T--){
		n=read();
		tot=0;
		for(int i=1;i<=n;i++)head[i]=0;
		for(int i=2;i<=n;i++)
			add(fa[i]=read(),i);
		for(int i=1;i<=n;i++)
			a[i]=read();
		solve();
	}
	return 0;
}
posted @ 2024-10-14 09:55  luckydrawbox  阅读(17)  评论(0)    收藏  举报  来源