题解:AT_agc076_c [AGC076C] Slime Eat Slime

参考了官方题解。

题意:给出一个图,再给出每个点的权 \(0\le a_u\le 2K\),如果相邻两个点 \(u,v\) 满足 \((a_u-a_v)\bmod (2K+1) \ge K+1\),那么 \(u\) 就可以把 \(v\) 干掉,问最后是否可以把 \(1\) 号点留下。\(\sum n,m\le 2.5\times 10^5\)

做法:

不妨令 \(a_1 = 0\)

感觉看上去完全没啥入手点,我们不妨先把 \(1\) 号点拉出来,那么剩余的图会被划分成若干个连通块,那么我们希望是每个连通块内部打完之后,剩下的都被 \(1\) 干掉,这样就可以了。

那么我们会这么想:每个连通块选出来若干个点给 \(1\) 干掉,然后给 \(1\) 干掉的那些点再递归去让他们干掉别的点,一层层下去,但是这样显然太难做了,每次要选择的点完全无法刻画。实际上我们发现,只需要考虑一层点就可以了。我们接下来对于一个连通块考虑。

我们注意到 \(1\) 可以干掉的是 \([1,K]\),假设有一个方案,我们就拉出来一个点 \(u\) 满足 \(a_u\in [1,K]\)\(a_u\) 最大,他就可以帮我们干掉 \([a_u+1,a_u+K]\) 的部分。我们考虑把点 \(u\) 作为我们被 \(1\) 干掉的点,这里我们不要求 \(a_u\) 最大,任意都可以。那么我们把 \(u\) 扔掉之后这个连通块会变成若干个小连通块,这些连通块就得满足一些条件才行:

  • \(a\) 全都在 \([1,K]\) 内。

  • 如果不满足上面这条,则必须满足存在于一个在 \([K+1, a_u+K]\) 的点,否则如果 \(a_u \ge K+1\) 的点全在 \([a_u+K+1,2K]\) 这一部分,这些点最后一定会剩下一个没法被干掉。

这些是必要条件,我们考虑证明他同时是充分的。

我们称 \(a_v\in [1,K]\) 的点 \(v\) 为 A 类点,\(a_v\in [K+1,a_u+K]\) 的称为 B 类点,\(a_v\in [a_u+K+1,2K+1]\) 的称为 C 类点。

如果全都是 A 类点,显然全用 \(1\) 干掉就可以了。

否则我们考虑我们需要干掉所有的 C 类点才能合法,我们发现只要我们不让 A 类点去干掉 B 类点,让 C 类点去随便和非 C 类点进行操作,我们发现 B 类点一定不会被干掉,这样最后会剩下若干 A 类点和 B 类点,我们让 \(1,u\) 去干掉就可以了。

那么现在就是怎么 check 一个点 \(u\) 合法不合法,发现这跟扔掉一个点之后拆出来的连通块有关,所以我们考虑建出来圆方树,我们可以把圆点作为答案,然后就是要检验自己的子树和外面的子树是否满足全为 A 类点或者存在 B 类点了,这个换根随便维护即可,复杂度 \(O(n+m)\)

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int n, m, tot, dfn[maxn], low[maxn], idx, pos[maxn], st[maxn], top, k, c[maxn];
vector<int> e[maxn], g[maxn];
void clear() {
	for (int i = 1; i <= 2 * n; i++)
		dfn[i] = low[i] = pos[i] = st[i] = 0, e[i].clear(), g[i].clear();
	top = idx = tot = 0;
}
int all;
void tarjan(int u, int fa) {
	dfn[u] = low[u] = ++idx; st[++top] = u; all++;
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		if(!dfn[v]) {
			tarjan(v, u);
			low[u] = min(low[u], low[v]);
			if(low[v] >= dfn[u]) {
				tot++;
				while(1) {
					int p = st[top--];
					g[tot].push_back(p), g[p].push_back(tot);
					if(p == v)
						break;
				}
				int p = u;
				g[tot].push_back(p), g[p].push_back(tot);
			}
		}
		else 
			low[u] = min(low[u], dfn[v]);
	}
}
int sz[maxn], mn[maxn], smn[maxn], p[maxn], val[maxn], ct[maxn], al[maxn];
void dfs1(int u, int fa) {
	sz[u] = ct[u] = (u <= n && (c[u] >= 1 && c[u] <= k)); al[u] = (u <= n);
	val[u] = (u <= n && (c[u] >= k + 1 && c[u] <= 2 * k + 1) ? c[u] : 2 * k + 2);
	mn[u] = val[u], smn[u] = 2 * k + 2; p[u] = u;
	for (int i = 0; i < g[u].size(); i++) {
		int v = g[u][i];
		if(v == fa)
			continue;
		dfs1(v, u);
		if(mn[v] < smn[u])
			smn[u] = mn[v];
		if(smn[u] < mn[u])
			p[u] = v, swap(smn[u], mn[u]);
		sz[u] += sz[v], al[u] += al[v];
	}
//	cout << u << " " << fa << " " << al[u] << " " << sz[u] << " " << c[u] << " " << k << " " << mn[u] << endl;
}
bool chk(int u, int fa, int mnv, int cnt) {
	if(!ct[u])
		return 0;
	int f = (cnt == all - al[u] || mnv <= c[u] + k);
//	cout << u << " " << f  << " " << all - al[u] << " " << cnt << endl;
	for (int i = 0; i < g[u].size(); i++) {
		int v = g[u][i];
		if(v == fa)
			continue;
		f &= (al[v] == sz[v] || mn[v] <= c[u] + k);
	}
	return f;
}
bool dfs2(int u, int fa, int mnv, int cnt) {
	if(chk(u, fa, mnv, cnt))
		return 1;
	mnv = min(mnv, val[u]);
	for (int i = 0; i < g[u].size(); i++) {
		int v = g[u][i];
		if(v == fa)
			continue;
		if(dfs2(v, u, min(mnv, (p[u] == g[u][i] ? smn[u] : mn[u])), cnt + sz[u] - sz[v]))
			return 1;
	}
	return 0;
}
bool solve(int u) {
	all = 0;
	tarjan(u, 0);
	dfs1(u, 0);
	if(dfs2(u, 0, 2 * k + 2, 0))
		return 1;
	return 0;
}
int cnt = 0;
void solve() {
	cnt++;
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
		cin >> c[i];
	for (int i = n; i >= 1; i--)
		c[i] = (c[i] - c[1] + 2 * k + 1) % (2 * k + 1);
	for (int i = 2; i <= n; i++)
		if(c[i] == 0)
			c[i] = 2 * k + 1;
//	for (int i = 1; i <= n; i++)
//		cout << c[i] << " ";
//	cout << endl;
	clear();
	tot = n;
	for (int i = 1; i <= m; i++) {
		int x, y; cin >> x >> y;
		if(x > y)
			swap(x, y);
		if(x == 1) 
			e[x].push_back(y);
		else
			e[x].push_back(y),
			e[y].push_back(x);
	}
	for (int i = 0; i < e[1].size(); i++)
		if(!dfn[e[1][i]]) {
			if(!solve(e[1][i])) {
				cout << "No" << endl;
				return ;
			}
		}
	cout << "Yes" << endl;
	return ;
}
signed main() {
//	freopen("test.in", "r", stdin);
//	freopen("std.out", "w", stdout);
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}
/*
1
5 8 10
5 6 10 8 19
1 2
1 3
1 4
1 5
2 3
2 4
3 4
3 5
*/
posted @ 2026-01-06 12:34  LUlululu1616  阅读(8)  评论(3)    收藏  举报