题解:P11189 「KDOI-10」水杯降温
题意
给出一棵 个节点的树,根节点为 ,节点 有一个点权 。你可以对这棵树进行如下操作:
- 将节点 的子树内的所有点点权增加 。
- 选择一个叶子 ,将 到根节点的链上所有点的点权减少 。
求是否能进行若干次操作,使整个树的点权全为 。
分析
点权全为 等价于根节点点权为 ,且任意一个非根节点与其父节点的差为 ;再结合操作修改时同时被修改的点之间的差不变,可以考虑从差分的角度分析操作。
令 ,那么一次操作 相当于将 减 ,若 为 则使 ,操作 相当于将链上所有点的不在链上的儿子的 减 ,同时使 减 。也就是说差分数组只会减小!由于最后 ,所以若开始时存在 ,则无解。
在满足 的情况下:
- ,那么我们完全可以只用 操作把 增加到 ,再用操作 把所有的 变为 即可。
- ,则必须用若干 操作将 减小到非正数,同时保证 非负。
考虑到交换操作顺序不影响答案,我们钦定所有 操作在 操作之前,这样问题就变成能否通过若干次操作 使树满足情况 的前置条件。
由于有 的限制,我们能进行的操作 的次数必然有上限,于是我们考虑求出这个上限 ,判断是否 即可,
设 表示节点 的子树内能进行的操作 的最大次数,若 为叶节点,显然 ,否则,二分出 。具体地,二分答案为 ,对于每个儿子 ,我们先执行 次 操作,即在保证 的情况下执行尽可能少的操作, 是其他儿子执行操作对 的影响,若某个 执行后不满足条件,或总执行次数超过了 ,则无解。否则,我们分配剩下的操作,随便地给 的 分配次数,能分配完则有解。这样的贪心策略正确性显然。
最终时间复杂度 , 为值域。
代码
考场代码,有一些无用的片段,请自动忽略。
#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;
}

浙公网安备 33010602011771号