CF1537F Figure Fixing 题解

题意:

你有一张 \(n\)\(m\) 边的无向连通图,第 \(i\) 个点上有点权 \(v_i\) 和目标值 \(t_i\)

在一次操作中,你可以选择一条边 \((i,j)\),并同时给 \(v_i\)\(v_j\) 增加一个任意整数值,可以为负。

你需要判断,这张图是否可以在有限步操作中,使得每个节点满足 \(v_i = t_i\)

\(n,m \le 2 \times 10^5\)

思路:

首先,将点权替换成 \(t_i - v_i\),转化为将所有点权变为 \(0\)。显然如果总和为奇数一定不可行。

对于一个奇环,我们发现,可以让任何一个节点在不改变别的点的情况下 \(+2,-2\)

这个结论我想记一下,大部分题解都用了这个结论。

但是这题可以完全用深搜树来做。

假设原图是一棵树,设 \(f_u\) 表示将 \(u\) 子树中的点全部消掉只剩 \(u\)\(u\) 的权值,显然 \(f_u = v_u - \sum_{v}f_v\),将这个拆开我们就会得到约束是奇数层的点权和等于偶数层的。

我们考虑选出原图的一棵生成树,则我们可以的目标就是对所有回边进行若干操作使得奇数层点权等于偶数层。如果存在一条回边连接的两个点层数奇偶性相同,这样我们就可以不断对这一类层的总和 \(+2,-2\),由于所有点权和是偶数,所以我们一定可以将其改到与另一个相等。

如果没有,那么对于一条回边用不用改变不了两种点点权和的差值,所以只能看初始状态。

总结一下发现就是分二分图和非二分图的情况讨论,然后用并查集判断一下即可。

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;

int n, m;
int a[N] = {0};

int p[N] = {0}, sz[N] = {0}, d[N] = {0};

void init() {
	for (int i = 1; i <= n; i++)
		p[i] = i, sz[i] = 1, d[i] = 0;
}
int fnd(int x) {
	if (p[x] == x)
		return x;
	int ans = fnd(p[x]);
	d[x] = (d[x] + d[p[x]]) % 2;
	return p[x] = ans;
}
bool unn(int x, int y) {
	int px = fnd(x), py = fnd(y);
	if (px == py)
		return d[x] != d[y];
	if (sz[px] < sz[py])
		swap(px, py), swap(x, y);
	p[py] = px;
	sz[px] += sz[py];
	d[py] = (d[x] + d[y] + 1) % 2;
	return true;
}


void slv() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	long long tot = 0ll;
	for (int i = 1, x; i <= n; i++)
		cin >> x, a[i] = x - a[i], tot += a[i];
	if (abs(tot) % 2 == 1) {
		for (int i = 1, u, v; i <= m; i++)
			cin >> u >> v;
		cout << "NO" << endl;
		return;
	}
	init();
	bool flg = false;
	for (int i = 1, u, v; i <= m; i++) {
		cin >> u >> v;
		if (flg)
			continue;	
		if (!unn(u, v)) 
			flg = true;
	}
	if (flg) {
		cout << "YES" << endl;
		return;
	}
	long long sum1 = 0ll, sum2 = 0ll;
	for (int i = 1; i <= n; i++)
		if (p[i] == i) {
			for (int j = 1; j <= n; j++) {
				fnd(j);
				sum1 += d[j] * a[j], sum2 += (1 - d[j]) * a[j];
			}
		}
	if (sum1 == sum2)
		cout << "YES" << endl;
	else
		cout << "NO" << endl;
}

int main() {
	int T;
	cin >> T;
	while (T--)
		slv();
	return 0;
} 
*/
posted @ 2024-03-14 21:46  rlc202204  阅读(27)  评论(0)    收藏  举报