【AtCoder Regular Contest 063 E】Integers on a Tree

贪心,构造。

题目大意:

给定一棵树,有些点已经有点权了。

现在要给剩下的点标上点权,要求满足:任意两个有边相连的结点,点权之差恰好为 $1$。

问是否有可行方案。如果有输出一种。

题解:

我们用一个堆来存放已经有点权的点,按照点权排序。

每次选择已经标号的点权最小的点,设点权为 $x$,并检查与它相连的点。如果有未标点权的,则令其点权为 $x+1$。如果有已经标上点权,但是差不为 $1$ 的,则说明无解。

感性理解:由于我们每次取出当前最小的结点,说明点权小于它的结点四周已经符合条件。那么我们只需要考虑点权大于等于 $x$ 的点。若标 $x-1$ 有合法方案,则标 $x+1$ 也能达到同样效果。反之不一定。由于我们一直在往大了标号,所以若发现冲突,则一定不存在合法方案。

Code:

#include<queue>
#include<cstdio>
#include<cstdlib>
const int N=1e5+6;
using namespace std;
struct data{
    int u,d;
    inline bool operator<(const data&rhs)const{return d>rhs.d;}
};
priority_queue<data>q;
int n,k,w[N];
bool vis[N];
vector<int>G[N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v),G[v].push_back(u);
    }
    scanf("%d",&k);
    if(!k)vis[1]=1,w[1]=114514,q.push((data){1,114514});
    while(k--){
        int u,v;
        scanf("%d%d",&u,&v),w[u]=v,vis[u]=1;
        q.push((data){u,v});
    }
    while(!q.empty()){
        int u=q.top().u;q.pop();
        for(int to:G[u])if(!vis[to])vis[to]=1,w[to]=w[u]+1,q.push((data){to,w[to]});else
        if(abs(w[to]-w[u])!=1)return puts("No"),0;
    }
    puts("Yes");
    for(int i=1;i<=n;++i)printf("%d\n",w[i]);
    return 0;
}