NOI Online 2020 Day1 T1 序列(并查集缩点+ 二分图染色 + 贪心)

Description

Link

Solution

对于操作二,如果有三个数 a 1 ∼ a 3 a_1 \sim a_3 a1a3 ( a 1 , a 2 , 2 ) (a_1,a_2, 2) (a1,a2,2) a ( a 2 , a 3 , 2 ) a(a_2,a_3,2) a(a2,a3,2),那么可以将 a 1 + 1 , a 2 − 1 , a 2 + 1 , a 3 − 1 ⇒ ( a 1 , a 3 , 2 ) a_1 + 1, a_2 - 1, a_2 + 1, a_3 - 1 \Rightarrow (a_1,a_3,2) a1+1,a21,a2+1,a31(a1,a3,2)。这意味着如果用并查集将有传递性的数加入一个联通块中,那么会有若干个联通块,每个联通块中任意两个数都可以进行操作二。那么如果一个联通块中需要加的值与需要减的值相同,那么一定是合法的。所以把每个联通块缩成一个点,点权为要加的值减去要减的值。

可能有的点权不合法,考虑操作一。如果有 ( a 1 , a 2 , 1 ) , ( a 1 , a 3 , 1 ) , ( a 2 , a 3 , 1 ) (a_1,a_2,1),(a_1,a_3,1),(a_2,a_3,1) (a1,a2,1),(a1,a3,1),(a2,a3,1),那么可以构成 a i ± 2 a_i \pm 2 ai±2,然后 a 1 , a 2 , a 3 a_1,a_2,a_3 a1,a2,a3 所在的联通块中任意一个数可以通过 a 1 , a 2 , a 3 a_1,a_2,a_3 a1,a2,a3 来白嫖 a i ± 2 a_i \pm 2 ai±2。如果将操作一对应的点连起来,那么这三个点构成了奇环,原图就不是二分图了,这个可以 bfs 或 dfs 染色求。

不过可能有自环,要特判一下。因为自环相当于 ( a 1 , a 2 , 1 ) (a_1,a_2,1) (a1,a2,1) ( a 1 , a 2 , 2 ) (a_1,a_2,2) (a1,a2,2) 那么 a 1 + 1 , a 2 − 1 , a 1 + 1 , a 2 + 1 a_1 + 1, a_2 - 1, a_1+1,a_2+1 a1+1,a21,a1+1,a2+1 同样也可以构成 a i ± 2 a_i \pm 2 ai±2

如果是二分图,那么两边只能同时加一或减一,所以有边相连的差值相同才能 YES。如果不是二分图,那么还可以白嫖加二或减二,所以有边相连的差值是偶数也是 YES。对于所有没边相连的点,点权必须为 0 0 0 才能是 YES。

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)。我没按秩合并 /kk。

Code

#include 
using namespace std;
#define int long long
const int N = 2e5 + 5, INF = 0x3f3f3f3f;
inline int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
struct edge{
	int to, nxt;
}e[N];
int head[N], tot;
void addedge(int x, int y) {
	e[++tot].to = y, e[tot].nxt = head[x], head[x] = tot; 
}
int a[N], b[N], f[N], t[N], x[N], y[N], val[N];
int find(int x) {
	if (f[x] == x) return x;
	return f[x] = find(f[x]);
}
void merge(int x, int y) {
	x = find(x), y = find(y);
	if (x != y) f[x] = y, val[y] += val[x]; 
} 
int col[N], sum, flg;
void dfs(int x, int c) {
	col[x] = c;
	if (c) sum += val[x];
	else sum -= val[x]; 
	for (int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to;
		if (col[y] == -1) dfs(y, c ^ 1);
		else if (col[x] == col[y]) flg = 1;
	}
}
signed main() {
	int T = read();
	while (T--) {
		memset(col, -1, sizeof(col)); 
		memset(head, 0, sizeof(head)); tot = 0;
		int n = read(), m = read();
		for (int i = 1; i <= n; i++) a[i] = read();
		for (int i = 1; i <= n; i++) b[i] = read();
		for (int i = 1; i <= n; i++) f[i] = i, val[i] = a[i] - b[i];
		for (int i = 1; i <= m; i++) {
			t[i] = read(), x[i] = read(), y[i] = read();
			if (t[i] == 2) merge(x[i], y[i]);
		}
		for (int i = 1; i <= m; i++) 
			if (t[i] == 1) addedge(find(x[i]), find(y[i])), addedge(find(y[i]), find(x[i]));
		bool ok = 1;
		for (int i = 1; i <= n; i++) 
			if (find(i) == i && col[i] == -1) {
				sum = flg = 0;
				dfs(i, 0); 
				for (int j = head[i]; j; j = e[j].nxt)
					if (e[j].to == i) {
						flg = 1; break;
					}
				if (head[i] == 0) ok &= (sum == 0);
				else if (flg) ok &= (sum % 2 == 0);
				else ok &= (sum == 0);
			}
		if (ok) puts("YES");
		else puts("NO");
	} 
	return 0;
}

你可能感兴趣的:(NOI Online 2020 Day1 T1 序列(并查集缩点+ 二分图染色 + 贪心))