【CodeForces】Codeforces Round 583

比赛链接

点击打开链接

官方题解

点击打开链接

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 Nx 个,那么
0 ≤ x ≤ b , 0 ≤ N − x ≤ g 0\leq x\leq b,0\leq N-x\leq g 0xb,0Nxg

求出 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 diN ,可以考虑将所有点对中的一个点串成一条链,并将对应的 d i d_i di 较大的排在左侧。

对于排在 i i i 处的点,令其对应的 d i d_i di x x x ,则将其对应的另一个点与排在 i + x − 1 i+x-1 i+x1 处的点连边,若 i + x − 1 i+x-1 i+x1 处的点是链的末尾,则将新加的点设为新的链的末尾。

由于 x x x 是不增的,不难证明排在 i + x − 1 i+x-1 i+x1 处的点一定存在。

时间复杂度 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)​ xy,xyM(xy) ,距离为 x − y ​ x-y​ xy a i ​ a_i​ ai 贡献为 x ​ x​ x
( 2 ) ​ (2)​ (2) x ≥ y , x − y &gt; M − ( x − y ) ​ x\geq y,x-y&gt; M-(x-y)​ xy,xy>M(xy) ,距离为 M − ( x − y ) ​ M-(x-y)​ M(xy) a i ​ a_i​ ai 贡献为 − x ​ -x​ x
( 3 ) (3) (3) x &lt; y , y − x ≤ M − ( y − x ) x&lt; y,y-x\leq M-(y-x) x<y,yxM(yx) ,距离为 y − x y-x yx a i a_i ai 贡献为 − x -x x
( 4 ) (4) (4) x &lt; y , y − x &gt; M − ( y − x ) x&lt; y,y-x&gt; M-(y-x) x<y,yx>M(yx) ,距离为 M − ( y − x ) M-(y-x) M(yx) 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 &gt; 2 k&gt;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+xk,d+yk ,则表明从该子树内出发向直径的两头均存在长度为 k k k 的路径,由于 k ≥ 3 k\geq 3 k3 ,一定存在两条分别延伸向两头的长度为 k k k 的路径经过的直径上的点颜色不同,从而此时问题无解。

否则,即 d + x ≥ k , d + y ≥ k ​ d+x\geq k,d+y\geq k​ d+xk,d+yk 中至少一个不成立,若 d + x ≥ k ​ d+x\geq k​ d+xk ,则可以将子树内深度为 i ​ i​ i 的点染为 c + i ​ c+i​ c+i ,否则,可以将子树内深度为 i ​ i​ i 的点染为 c − i ​ c-i​ ci ,这里的加减需要考虑取模。注意我们是不需要考虑子树内的长度为 k ​ k​ k 的路径的,因为以上两者的不成立同样说明了 2 d + 1 &lt; k ​ 2d+1&lt;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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值