【ZJNU 组队赛 12-05:树上DP】Integers on a Tree | AtCoder - arc063_c

Integers on a Tree

题外话

这题数据有点问题,这里更正了一下。

难度

4 / 10 4/10 4/10
其实就是一个简单的树上DP

题意

给定一颗 N N N 个节点的树。
其中 K K K 个节点已经标上了数字 P i P_i Pi
问你能否让空节点标上一些数字后,所有相邻节点的数字差为 1 1 1
若能,请输出一种涂法。

注意:所有数字都是整数。自己涂的数字可以超过 1 0 6 10^6 106,也可以是负数。

数据范围

1 ≤ N ≤ 1 0 5 1\le N\le 10^5 1N105
1 ≤ K ≤ N 1\le K\le N 1KN
0 ≤ P i ≤ 1 0 6 0\le P_i\le 10^6 0Pi106

思路

  • 因为是一棵树,我们不妨以 1 1 1 作为根节点,进行考虑。
  • 考虑每一层,易得:每一层必须同奇或者同偶,相邻的两层奇偶性必须不同
  • 考虑父节点 x x x 与它的一些儿子节点 v v v。首先,每一个节点都有一段可能合法的范围,易得范围一定是连续的一段奇数或者是偶数。
  • 如果 v i = [ a i , b i ] v_i=[a_i,b_i] vi=[ai,bi] 是该节点的合法范围的话,那么父亲节点的范围必须能够同时满足和他们之间的差值为 1 1 1,即 父亲节点的范围为 ⋂ [ a i − 1 , b i + 1 ] \bigcap[a_i-1,b_i+1] [ai1,bi+1]
  • 如果区间范围不合法了,或者奇偶性不合法了,那么就无答案。
  • 若有答案,我们每次都可以选择父节点的左区间顶点,然后儿子节点的区间进行更新即可。

核心代码

时间复杂度 O ( N ) O(N) O(N)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 1e5+50;
const int INF = 0x3f3f3f3f;


int lef[MAX];			/// 记录节点的可选区间范围
int rig[MAX];
int jo[MAX];			/// 记录节点的奇偶性。0:未知;1:奇数;2:偶数
vector<int>V[MAX];		/// 存图
bool flag;				/// 存合法性

void dfs(int x,int fa){
    
    
    int lmx = -INF;
    int rmn = INF;
    bool ji = false;
    bool ou = false;
    for(auto it : V[x]){
    
    
        if(it == fa)continue;
        dfs(it,x);
        if(jo[it] == 1)ji = true;
        else if(jo[it] == 2)ou = true;
        lmx = max(lmx,lef[it]-1);
        rmn = min(rmn,rig[it]+1);
    }
    if((ji && ou) || (ji && jo[x] == 1) || (ou && jo[x] == 2) || lmx > rmn || lmx > rig[x] || rmn < lef[x]){
    
    	/// 奇偶性不合法或者区间范围不合法
        flag = false;
        return ;
    }else{
    
    					/// 更新奇偶性 和区间的交
        if(ji)jo[x] = 2;
        else if(ou)jo[x] = 1;

        lef[x] = max(lef[x],lmx);
        rig[x] = min(rig[x],rmn);
    }
}
int ans[MAX];			/// 存每个节点的数字答案。
void dfs2(int x,int fa){
    
    
    ans[x] = lef[x];	/// 取左端点。
    for(auto it : V[x]){
    
    
        if(it == fa)continue;
        if(lef[x] - 1 >= lef[it] && lef[x] - 1 <= rig[it]){
    
    			/// 更新儿子区间
            lef[it] = lef[x] - 1;
        }else{
    
    
            lef[it] = lef[x] + 1;
        }
        dfs2(it,x);
    }
}
int main()
{
    
    
    flag = true;
    int N;scanf("%d",&N);
    for(int i = 1;i <= N;++i)
        lef[i] = -INF, rig[i] = INF,jo[i] = 0;
    for(int i = 1;i < N;++i){
    
    
        int ta,tb;scanf("%d%d",&ta,&tb);
        V[ta].push_back(tb);
        V[tb].push_back(ta);
    }
    int K;scanf("%d",&K);
    if(K == 0)while(1);
    for(int i = 0;i < K;++i){
    
    
        int ta,tb;scanf("%d%d",&ta,&tb);
        lef[ta] = rig[ta] = tb;
        if(tb & 1)jo[ta] = 1;
        else jo[ta] = 2;
    }
    dfs(1,1);
    if(!flag)printf("No");
    else {
    
    
        printf("Yes\n");
        dfs2(1,1);
        for(int i = 1;i <= N;++i){
    
    
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/110714817