题外话
这题数据有点问题,这里更正了一下。
难度
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 1≤N≤105
1 ≤ K ≤ N 1\le K\le N 1≤K≤N
0 ≤ P i ≤ 1 0 6 0\le P_i\le 10^6 0≤Pi≤106
思路
- 因为是一棵树,我们不妨以 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] ⋂[ai−1,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;
}