[CF1214H] Tiles Placement
[题目链接] :
https://codeforces.com/contest/1214/problem/H
[题解] :
首先判断不合法的情况 :
如图 , 若 \(a + b \geq k - 1 , b + c \geq k - 1 , a + c \geq k - 1\) , 显然没有任何一种方案可以满足条件。
这种情况可以通过动态规划判断。
可以证明 , 除了这种情况 , 其它情况都是合法的。 下面给出一种具体的构造方案。
\(1.\) 找出树的直径。
\(2.\) 将树的直径平均分为两部分。
\(3.\) 将直径上的点用 \(1 , 2 , 3 , ... k , 1 , 2 , 3 , ... k\) 交替染色。 不在直径上的点 , 如果在直径的左侧部分 , 那
么就用 \(k , k - 1 , ... 1\) 递减染色 , 否则用 \(2 , 3 , ... k\) 递增染色。
如图 :
下面证明这个构造的正确性 , 考虑反证 , 设有一条路径不合法。
情况 \(1\) : 不合法的路径与直径不交 , 如图 , 由直径的性质 , 蓝色部分的长度比 \(v\) 左右两侧的路径都要长。 因此这样的情况
根本不存在。
情况 \(2\) : 不合法的路径与直径一侧有交。 这样的情况也不存在 , 和情况 \(1\) 类似。
情况 \(3\) : 不合法的路径跨过直径的两部分 , 那么根据刚才的染色方案 , 这样的情况不可能发生。
时间复杂度 : \(O(N)\)
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 5e5 + 5;
struct Edge {
int to , nxt;
} E[MN << 1];
int N , K , r1 , r2 , d1 , d2 , tot;
int head[MN] , s1[MN] , s2[MN];
inline void AddEdge(int u , int v) {
E[++tot] = (Edge) {v , head[u]};
head[u] = tot;
}
inline void dfs1(int x , int fa , int dep) {
if (dep > d1) r1 = x , d1 = dep;
for (int i = head[x]; i; i = E[i].nxt) if (E[i].to != fa)
dfs1(E[i].to , x , dep + 1);
}
inline void dfs2(int x) {
if (s1[x] > s1[r2]) r2 = x;
for (int i = head[x]; i; i = E[i].nxt) if (!s1[E[i].to]) {
s1[E[i].to] = s1[x] + 1;
dfs2(E[i].to);
}
}
inline void dfs3(int x) {
for (int i = head[x]; i; i = E[i].nxt) if (!s2[E[i].to]) {
s2[E[i].to] = s2[x] - 1;
dfs3(E[i].to);
}
}
int main() {
scanf("%d%d" , &N , &K);
for (int i = 1; i < N; ++i) {
int u , v;
scanf("%d%d" , &u , &v);
AddEdge(u , v); AddEdge(v , u);
}
dfs1(1 , 0 , 1);
s1[r1] = 1; dfs2(r1);
s2[r2] = s1[r2]; dfs3(r2);
for (int i = 1; i <= N; ++i)
if (s1[i] >= K && s2[r2] - s2[i] + 1 >= K && s1[i] % K != s2[i] % K) {
puts("No");
return 0;
}
puts("Yes");
for (int i = 1; i <= N; ++i) {
if (s1[i] >= s2[r2] - s2[i] + 1) printf("%d" , (s1[i] - 1) % K + 1);
else printf("%d" , (s2[i] - 1) % K + 1);
putchar((i == N) ? '\n' : ' ');
}
return 0;
}
`