NOI Online 2020 Day1 T1 序列(並查集縮點+ 二分圖染色 + 貪心)

Description

Link

Solution

對於操作二,如果有三個數 a1a3a_1 \sim a_3(a1,a2,2)(a_1,a_2, 2)a(a2,a3,2)a(a_2,a_3,2),那麼可以將 a1+1,a21,a2+1,a31(a1,a3,2)a_1 + 1, a_2 - 1, a_2 + 1, a_3 - 1 \Rightarrow (a_1,a_3,2)。這意味着如果用並查集將有傳遞性的數加入一個聯通塊中,那麼會有若干個聯通塊,每個聯通塊中任意兩個數都可以進行操作二。那麼如果一個聯通塊中需要加的值與需要減的值相同,那麼一定是合法的。所以把每個聯通塊縮成一個點,點權爲要加的值減去要減的值。

可能有的點權不合法,考慮操作一。如果有 (a1,a2,1),(a1,a3,1),(a2,a3,1)(a_1,a_2,1),(a_1,a_3,1),(a_2,a_3,1),那麼可以構成 ai±2a_i \pm 2,然後 a1,a2,a3a_1,a_2,a_3 所在的聯通塊中任意一個數可以通過 a1,a2,a3a_1,a_2,a_3 來白嫖 ai±2a_i \pm 2。如果將操作一對應的點連起來,那麼這三個點構成了奇環,原圖就不是二分圖了,這個可以 bfs 或 dfs 染色求。

不過可能有自環,要特判一下。因爲自環相當於 (a1,a2,1)(a_1,a_2,1)(a1,a2,2)(a_1,a_2,2) 那麼 a1+1,a21,a1+1,a2+1a_1 + 1, a_2 - 1, a_1+1,a_2+1 同樣也可以構成 ai±2a_i \pm 2

如果是二分圖,那麼兩邊只能同時加一或減一,所以有邊相連的差值相同才能 YES。如果不是二分圖,那麼還可以白嫖加二或減二,所以有邊相連的差值是偶數也是 YES。對於所有沒邊相連的點,點權必須爲 00 才能是 YES。

時間複雜度 O(nlogn)O(n \log n)。我沒按秩合併 /kk。

Code

#include <bits/stdc++.h>
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章