比赛链接
官方题解
Problem A. Optimal Currency Exchange
注意到美元所有的面值均为 1 1 1 的倍数,不妨认为只可以兑换 1 1 1 美元。
类似地,也可以认为只可以兑换 5 5 5 欧元。
则我们需要使得 x d + 5 y e xd+5ye xd+5ye 尽可能接近 N N N 。
枚举 y y y ,并用除法计算可能到达的最近的值即可。
时间复杂度 O ( N e ) O(\frac{N}{e}) O(eN) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int main() {
int n, a, b;
read(n), read(a), read(b);
int ans = n; b *= 5;
while (n >= 0) {
chkmin(ans, n % a);
n -= b;
}
writeln(ans);
return 0;
}
Problem B. Badges
设男生
x
x
x 个,女生
N
−
x
N-x
N−x 个,那么
0
≤
x
≤
b
,
0
≤
N
−
x
≤
g
0\leq x\leq b,0\leq N-x\leq g
0≤x≤b,0≤N−x≤g
求出 x x x 的整数解的个数即可。
时间复杂度 O ( 1 ) O(1) O(1) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int main() {
int a, b, n;
read(a), read(b), read(n);
int l = 0, r = n;
chkmin(r, a);
chkmax(l, n - b);
if (l > r) writeln(0);
else writeln(r - l + 1);
return 0;
}
Problem C. Bad Sequence
一个括号序列合法的充要条件是:将 ( 看做 + 1 +1 +1 , ) 看做 − 1 -1 −1 ,序列的前缀和始终非负,且总和为 0 。
因此,直接将最右侧的一个 ( 提到序列开头判断序列是否合法即可。
时间复杂度 O ( N ) O(N) O(N) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
char s[MAXN];
int main() {
int n, pos = 0; read(n);
scanf("\n%s", s + 1);
for (int i = 1; i <= n; i++)
if (s[i] == '(') pos = i;
if (pos != 0) {
for (int i = pos; i >= 2; i--)
s[i] = s[i - 1];
s[1] = '(';
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == '(') cnt++;
else cnt--;
if (cnt < 0) {
puts("No");
return 0;
}
}
if (cnt == 0) puts("Yes");
else puts("No");
return 0;
}
Problem D. Treasure Island
不难发现答案不超过 2 2 2 ,因为我们一定可以通过阻挡与起点相邻的格子来达成目标。
特判初始时已经无法到达终点的情况,我们只需要判断答案是 1 1 1 还是 2 2 2 。
可以通过优先考虑向右走找到最靠右上的一条路径,通过优先考虑向下走找到最靠左下的一条路径,若这两条路径交于某点,则说明所有路径都必须经过该点,从而答案为 1 1 1 ,否则,答案为 2 2 2 。
时间复杂度 O ( N × M ) O(N\times M) O(N×M) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m;
vector <char> s[MAXN];
vector <bool> a[MAXN];
vector <bool> b[MAXN];
vector <bool> vis[MAXN];
bool dfs1(int x, int y) {
vis[x][y] = a[x][y] = true;
if (x + y == n + m) return true;
if (x < n && !vis[x + 1][y] && s[x + 1][y] != '#') {
if (dfs1(x + 1, y)) return true;
}
if (y < m && !vis[x][y + 1] && s[x][y + 1] != '#') {
if (dfs1(x, y + 1)) return true;
}
a[x][y] = false;
return false;
}
bool dfs2(int x, int y) {
vis[x][y] = b[x][y] = true;
if (x + y == n + m) return true;
if (y < m && !vis[x][y + 1] && s[x][y + 1] != '#') {
if (dfs2(x, y + 1)) return true;
}
if (x < n && !vis[x + 1][y] && s[x + 1][y] != '#') {
if (dfs2(x + 1, y)) return true;
}
b[x][y] = false;
return false;
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++) {
s[i].push_back(' ');
static char tmp[MAXN];
scanf("\n%s", tmp + 1);
for (int j = 1; j <= m; j++)
s[i].push_back(tmp[j]);
a[i].resize(m + 1);
b[i].resize(m + 1);
vis[i].resize(m + 1);
}
if (!dfs1(1, 1)) {
puts("0");
return 0;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
vis[i][j] = false;
dfs2(1, 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (i + j == 2 || i + j == n + m) continue;
if (a[i][j] && b[i][j]) {
puts("1");
return 0;
}
}
puts("2");
return 0;
}
Problem E. Petya and Construction Set
注意到 d i ≤ N d_i\leq N di≤N ,可以考虑将所有点对中的一个点串成一条链,并将对应的 d i d_i di 较大的排在左侧。
对于排在 i i i 处的点,令其对应的 d i d_i di 为 x x x ,则将其对应的另一个点与排在 i + x − 1 i+x-1 i+x−1 处的点连边,若 i + x − 1 i+x-1 i+x−1 处的点是链的末尾,则将新加的点设为新的链的末尾。
由于 x x x 是不增的,不难证明排在 i + x − 1 i+x-1 i+x−1 处的点一定存在。
时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n; vector <int> s;
pair <int, int> a[MAXN];
int main() {
read(n);
for (int i = 1; i <= n; i++)
read(a[i].first), a[i].second = 2 * i - 1;
sort(a + 1, a + n + 1);
reverse(a + 1, a + n + 1);
s.resize(1);
for (int i = 1; i <= n; i++)
s.push_back(a[i].second);
for (int i = 1; i <= n - 1; i++)
printf("%d %d\n", a[i].second, a[i + 1].second);
for (int i = 1; i <= n; i++) {
int j = i + a[i].first - 1;
printf("%d %d\n", a[i].second + 1, s[j]);
if (j == s.size() - 1) s.push_back(a[i].second + 1);
}
return 0;
}
Problem F. Employment
不妨先将 a i , b i a_i,b_i ai,bi 按大小排序。
考虑固定 a 1 a_1 a1 匹配 b i b_i bi ,那么一定存在一种最优的匹配使得 a 2 a_2 a2 匹配了 b i + 1 b_{i+1} bi+1 , a 3 a_3 a3 匹配了 b i + 2 b_{i+2} bi+2 等等。这一点可以通过交换一个相邻的逆序匹配不会使答案变劣来证明。
本质上我们需要考虑 N N N 种匹配方式,即对于 x ∈ [ 0 , N ) x\in[0,N) x∈[0,N) ,让 a i a_i ai 匹配 b i + x b_{i+x} bi+x 。
考虑单个元素
a
i
a_i
ai 对每一种匹配方式的距离贡献,不妨令
a
i
=
x
a_i=x
ai=x ,所匹配的
b
i
=
y
b_i=y
bi=y ,则有以下四种情况:
(
1
)
(1)
(1) 、
x
≥
y
,
x
−
y
≤
M
−
(
x
−
y
)
x\geq y,x-y\leq M-(x-y)
x≥y,x−y≤M−(x−y) ,距离为
x
−
y
x-y
x−y ,
a
i
a_i
ai 贡献为
x
x
x 。
(
2
)
(2)
(2) 、
x
≥
y
,
x
−
y
>
M
−
(
x
−
y
)
x\geq y,x-y> M-(x-y)
x≥y,x−y>M−(x−y) ,距离为
M
−
(
x
−
y
)
M-(x-y)
M−(x−y) ,
a
i
a_i
ai 贡献为
−
x
-x
−x 。
(
3
)
(3)
(3) 、
x
<
y
,
y
−
x
≤
M
−
(
y
−
x
)
x< y,y-x\leq M-(y-x)
x<y,y−x≤M−(y−x) ,距离为
y
−
x
y-x
y−x ,
a
i
a_i
ai 贡献为
−
x
-x
−x 。
(
4
)
(4)
(4) 、
x
<
y
,
y
−
x
>
M
−
(
y
−
x
)
x< y,y-x> M-(y-x)
x<y,y−x>M−(y−x) ,距离为
M
−
(
y
−
x
)
M-(y-x)
M−(y−x) ,
a
i
a_i
ai 贡献为
x
x
x 。
可以将距离中的 M M M 的贡献摊到 a i a_i ai 或 b i b_i bi 上。
注意到四种情况对应的匹配方式 x x x 均为一个区间,可以用差分算出所有 x x x 对应的距离。
时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m, va[MAXN], vb[MAXN]; ll s[MAXN];
pair <int, int> a[MAXN], b[MAXN];
void add(int l, int r, ll x) {
if (l < 0 && r < 0) {
l += n;
r += n;
}
if (l < 0) s[l + n] += x, l = 0;
s[l] += x, s[r + 1] -= x;
}
int main() {
read(m), read(n);
for (int i = 1; i <= n; i++)
read(a[i].first), a[i].second = i;
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
read(b[i].first), b[i].second = i;
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
va[i] = a[i].first;
vb[i] = b[i].first;
}
for (int i = 1; i <= n; i++) {
int x = b[i].first;
if (x - 1 <= m - (x - 1)) {
int l = 1, r = x;
int ql = lower_bound(va + 1, va + n + 1, l) - va;
int qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, x);
} else {
int l = x - m / 2, r = x;
int ql = lower_bound(va + 1, va + n + 1, l) - va;
int qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, x);
r = l - 1, l = 1;
ql = lower_bound(va + 1, va + n + 1, l) - va;
qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, m - x);
}
if (m - x <= m - (m - x)) {
int l = x + 1, r = m;
int ql = lower_bound(va + 1, va + n + 1, l) - va;
int qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, -x);
} else {
int l = x + 1, r = x + m / 2;
int ql = lower_bound(va + 1, va + n + 1, l) - va;
int qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, -x);
l = r + 1, r = m;
ql = lower_bound(va + 1, va + n + 1, l) - va;
qr = upper_bound(va + 1, va + n + 1, r) - va - 1;
add(i - qr, i - ql, m + x);
}
}
for (int i = 1; i <= n; i++) {
int x = a[i].first;
if (x - 1 <= m - (x - 1)) {
int l = 1, r = x - 1;
int ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
int qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, x);
} else {
int l = x - m / 2, r = x - 1;
int ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
int qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, x);
r = l - 1, l = 1;
ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, -x);
}
if (m - x <= m - (m - x)) {
int l = x, r = m;
int ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
int qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, -x);
} else {
int l = x, r = x + m / 2;
int ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
int qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, -x);
l = r + 1, r = m;
ql = lower_bound(vb + 1, vb + n + 1, l) - vb;
qr = upper_bound(vb + 1, vb + n + 1, r) - vb - 1;
add(ql - i, qr - i, x);
}
}
ll ans = 1e18; int pos = 0;
for (int i = 0; i <= n - 1; i++) {
if (i != 0) s[i] += s[i - 1];
if (s[i] < ans) {
ans = s[i];
pos = i;
}
}
writeln(ans);
static int res[MAXN];
for (int i = 1; i <= n; i++)
if (i + pos <= n) res[a[i].second] = b[i + pos].second;
else res[a[i].second] = b[i + pos - n].second;
for (int i = 1; i <= n; i++)
printf("%d ", res[i]);
return 0;
}
Problem G. Feeling Good
考虑给定两行 i , j i,j i,j ,如何判断能否在这两行找到所需结构。
我们需要分别找到一列,使得这两行对应的位置分别为 ( 0 , 1 ) , ( 1 , 0 ) (0,1),(1,0) (0,1),(1,0) 。
若用二进制数 a i , a j a_i,a_j ai,aj 表示行 i , j i,j i,j ,则能够找到所需结构的充要条件是 a i a_i ai 不包含 a j a_j aj ,且 a j a_j aj 不包含 a i a_i ai 。
注意到 a i a_i ai 包含 a j a_j aj 仅当 a i a_i ai 中 1 1 1 的个数不少于 a j a_j aj 中 1 1 1 的个数,对于一个确定的局面,可以将所有行按照 1 1 1 的个数排序,然后便只需要检查相邻的两行就可以检查全局中是否存在所需结构。
对于存在修改的情况,只需要用 set 维护按照 1 1 1 的个数排序的数组即可。
用 bitset 实现修改和包含的判断,时间复杂度 O ( Q ( L o g N + M w ) ) O(Q(LogN+\frac{M}{w})) O(Q(LogN+wM)) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2048;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
set <int> ans;
int n, m, q, nxt[MAXN];
bitset <MAXN> a[MAXN], b[MAXN];
set <pair <int, int>> st;
void del(int x, int y) {
assert(nxt[x] == y), nxt[x] = 0;
if ((a[y] | a[x]) != a[y]) ans.erase(x);
}
void ins(int x, int y) {
assert(nxt[x] == 0), nxt[x] = y;
if ((a[y] | a[x]) != a[y]) ans.insert(x);
}
void del(int pos) {
pair <int, int> val = make_pair(a[pos].count(), pos);
auto tmp = st.lower_bound(val), suf = tmp; suf++;
if (tmp != st.begin()) {
auto pre = tmp; pre--;
del((*pre).second, (*tmp).second);
if (suf != st.end()) {
ins((*pre).second, (*suf).second);
}
}
if (suf != st.end()) {
del((*tmp).second, (*suf).second);
}
st.erase(tmp);
}
void ins(int pos) {
pair <int, int> val = make_pair(a[pos].count(), pos);
auto tmp = st.insert(val).first, suf = tmp; suf++;
if (tmp != st.begin()) {
auto pre = tmp; pre--;
if (suf != st.end()) {
del((*pre).second, (*suf).second);
}
ins((*pre).second, (*tmp).second);
}
if (suf != st.end()) {
ins((*tmp).second, (*suf).second);
}
}
int main() {
read(n), read(m), read(q);
for (int i = 1; i <= m; i++) {
b[i] = b[i - 1];
b[i].set(i);
}
for (int i = 1; i <= n; i++)
ins(i);
for (int i = 1; i <= q; i++) {
int x, l, r;
read(x), read(l), read(r);
del(x), a[x] ^= b[r] ^ b[l - 1], ins(x);
if (ans.size()) {
int x = *ans.begin(), y = nxt[x];
int l = (a[x] & ~a[y])._Find_first(), r = (~a[x] & a[y])._Find_first();
if (x > y) swap(x, y);
if (l > r) swap(l, r);
printf("%d %d %d %d\n", x, l, y, r);
} else puts("-1");
}
return 0;
}
Problem H. Tiles Placement
首先特判 k = 2 k=2 k=2 的情况,此时我们只需要对树进行二分图染色即可。
对于 k > 2 k>2 k>2 的情况,我们可以首先考虑树的直径。若树的直径长度不足 k k k ,那么树中就不存在长度为 k k k 的路径,从而任意染色方案都是合法的。否则,直径中每一个长度为 k k k 的段颜色都要互不相同,因此直径上的染色方式是固定的,即按照 1 , 2 , … , k , 1 , 2 , … , k , … 1,2,\dots,k,1,2,\dots,k,\dots 1,2,…,k,1,2,…,k,… 进行染色。
考虑直径上存在子树的各点
i
i
i ,令其到直径的两段的点数为
x
,
y
x,y
x,y ,颜色为
c
c
c ,子树的最大深度为
d
d
d 。
若
d
+
x
≥
k
,
d
+
y
≥
k
d+x\geq k,d+y\geq k
d+x≥k,d+y≥k ,则表明从该子树内出发向直径的两头均存在长度为
k
k
k 的路径,由于
k
≥
3
k\geq 3
k≥3 ,一定存在两条分别延伸向两头的长度为
k
k
k 的路径经过的直径上的点颜色不同,从而此时问题无解。
否则,即 d + x ≥ k , d + y ≥ k d+x\geq k,d+y\geq k d+x≥k,d+y≥k 中至少一个不成立,若 d + x ≥ k d+x\geq k d+x≥k ,则可以将子树内深度为 i i i 的点染为 c + i c+i c+i ,否则,可以将子树内深度为 i i i 的点染为 c − i c-i c−i ,这里的加减需要考虑取模。注意我们是不需要考虑子树内的长度为 k k k 的路径的,因为以上两者的不成立同样说明了 2 d + 1 < k 2d+1<k 2d+1<k ,从而子树内不存在长度为 k k k 的路径。
时间复杂度 O ( N ) O(N) O(N) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, k, x, y, ans[MAXN], d[MAXN];
int tot, rk[MAXN];
vector <int> a[MAXN];
void dfsc(int pos, int fa, int c) {
ans[pos] = c;
for (auto x : a[pos])
if (x != fa) dfsc(x, pos, 3 - c);
}
void dfsd(int pos, int fa, int dist) {
d[pos] = dist;
for (auto x : a[pos])
if (x != fa) dfsd(x, pos, dist + 1);
}
bool findpath(int pos, int fa) {
if (pos == y) {
rk[pos] = ++tot;
return true;
}
for (auto x : a[pos])
if (x != fa && findpath(x, pos)) {
rk[pos] = ++tot;
return true;
}
return false;
}
int maxdepth(int pos, int fa, int d) {
int res = d;
for (auto x : a[pos])
if (x != fa && ans[x] == 0) chkmax(res, maxdepth(x, pos, d + 1));
return res;
}
void colour(int pos, int fa, int cur, int d) {
ans[pos] = cur;
cur += d;
if (cur > k) cur -= k;
if (cur < 1) cur += k;
for (auto x : a[pos])
if (x != fa && ans[x] == 0) colour(x, pos, cur, d);
}
int main() {
read(n), read(k);
for (int i = 1; i <= n - 1; i++) {
int x, y; read(x), read(y);
a[x].push_back(y);
a[y].push_back(x);
}
if (k == 2) {
puts("Yes");
dfsc(1, 0, 1);
for (int i = 1; i <= n; i++)
printf("%d ", ans[i]);
return 0;
}
dfsd(1, 0, 1);
for (int i = 1; i <= n; i++)
if (d[i] > d[x]) x = i;
dfsd(x, 0, 1);
for (int i = 1; i <= n; i++)
if (d[i] > d[y]) y = i;
findpath(x, 0);
for (int i = 1; i <= n; i++)
if (rk[i]) ans[i] = rk[i] % k + 1;
for (int i = 1; i <= n; i++)
if (rk[i]) {
int d = maxdepth(i, 0, 0);
if (d != 0) {
if (d + rk[i] >= k && d + tot - rk[i] + 1 >= k) {
puts("No");
return 0;
}
if (d + rk[i] >= k) colour(i, 0, ans[i], 1);
else colour(i, 0, ans[i], -1);
}
}
puts("Yes");
for (int i = 1; i <= n; i++)
printf("%d ", ans[i]);
return 0;
}