1388C - Uncle Bogdan and Country Happiness(假装拓扑排序)

题意:起初所有人都在1号城市,总共有m个人,每个城市有pi个人,每个城市都有一个幸福指数:快乐的人减去不快乐的人。给出n-1条路:u,v,代表城市u和城市v有一条双向边,每个人回到自己的城市是走最短路,也就是不会经过一个节点两遍,每个人在路途中可能变得不快乐,以后的路不能变得快乐,问给出的数据能不能满足每个城市的幸福指数。

思路:我们可以从叶子节点下手,比如现在有一个3号叶子节点,p[3]即该城市人数,因为它是叶子节点,所以也就只有p[3]个人经过,然后我们就可以通过p[3]和h[3]来计算开心人数和不开心人数,假设开心人数为 x x x,不开心人数为 y y y,则
x + y = p [ 3 ] x+y=p[3] x+y=p[3]

x − y = h [ 3 ] x-y=h[3] xy=h[3]
于是
x = p [ 3 ] + h [ 3 ] 2 x=\frac{p[3]+h[3]}{2} x=2p[3]+h[3]
y = p [ 3 ] − h [ 3 ] 2 = p [ i ] − x y=\frac{p[3]-h[3]}{2}=p[i]-x y=2p[3]h[3]=p[i]x
注意这里 p [ 3 ] + h [ 3 ] p[3]+h[3] p[3]+h[3]要是偶数,否则无解输出NO
然后呢我们得到了该叶子节点的开心人数 x x x和不开心人数 y y y有什么用呢,我们可以把开心和不开心的人数传递上去上一个节点,然后上一个节点就有了一个约束即开心的人数要大于等于 x x x,把该叶子节点的人数也传上去,即上一个节点的人数+p[3],然后判断一下在去掉这个叶子节点后上一个节点是否变为叶子节点,是即进入队列

#include
using namespace std;
const int MAXN=1e5+10;
int m,n,k;
long long h[MAXN],p[MAXN];
long long good[MAXN],dot[MAXN];
vector<int>to[MAXN];
int in[MAXN];
bool vis[MAXN];
bool q(){
    queue<int>q;
    for(int i=1;i<=n;i++){
        if(i==1&&in[i]==0||i!=1&&in[i]==1){//节点1只有当入度为0才算为叶子节点
            q.push(i);
        }
    }
    while(!q.empty()){
        int now=q.front();q.pop();
        if(vis[now])continue;
        vis[now]=true;
        long long sum=good[now]+dot[now]+p[now];//该城市经过了sum个人,good[now]为后面的城市必须有good[now]个开心的人,dot[now]为后面城市有dot[now]个不开心的人,这个不开心的人可以是经过now城市前就不开心了或者经过now后才不开心的,无所谓
        if((sum+h[now])%2)return false;
        if(h[now]>0&&h[now]>sum||h[now]<0&&-h[now]>sum) return false;//小坑
        long long x=(sum+h[now])/2;
        long long y=sum-x;
        if(x<good[now])return false;//如果经过now城市开心人数不足以补给给后面城市开心人数即返回false
        for(auto i:to[now]){
            if(vis[i])continue;
            in[i]--;
            good[i]+=x;
            dot[i]+=y;
            if(in[i]==1&&i!=1||in[i]==0)//节点1只有当入度为0才算为叶子节点
                q.push(i);
        }
    }
    return true;
}
void solve() {
    if(q()) printf("YES\n");
    else printf("NO\n");
}
void init(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",p+i);
        to[i].clear();
        in[i]=0;
        good[i]=0;
        dot[i]=0;
        vis[i]=false;
    }
    for(int i=1;i<=n;i++)
        scanf("%lld",h+i);
    for(int i=1,u,v;i<n;i++){
        scanf("%d%d",&u,&v);
        to[u].push_back(v);
        to[v].push_back(u);
        in[u]++;
        in[v]++;
    }
}

signed main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        solve();
    }
    return 0;
}

你可能感兴趣的:(ACM水题)