P6185 [NOI Online #1 提高组] 序列 题解
一、题目:
二、思路:
这道题是一道蓝题,我都没想出来😥。我真的是菜到家了,今年的 NOI 不打铁都奇怪。
首先可以发现,操作 2 具有可传递性。若 \((a,b)\) 可以有操作 2,\((b,c)\) 也可以有操作 2,那么 \((a,c)\) 也可以等价地相当于有操作 2。所以我们可以将操作 1 和 2 看做两种无向边,使用并查集将第二种边的连通块缩成一个点。缩完点之后的图就只剩了第一种边。(注意,缩完点后的图是有自环的。)
设缩完点后的点权 \(\sum\limits b_x-a_x\)。
又可以发现,若第一种边形成了一个奇环,那么就可以在任意两点同时 +1 或 -1,也可以在任意两点一加一减。所以,如果环的点权之和为偶数,就可以完成目标;若第二种边形成了一个偶环,则可以看成是二分图。二分图的左部点的点权和和右部点的点权和之差是个定值,所以如果两部的点权和相等,就可以完成目标。(特别地,自环是奇环,所以自环并不影响正确性。)
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 1e5 + 5;
int n, m, a[MAXN], b[MAXN], fa[MAXN], idx, c[MAXN];
long long sum[MAXN], sumc[3];
pair<int, int>optb[MAXN];
vector<int>linker[MAXN];
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
x = find(x); y = find(y);
if (x != y) fa[x] = y;
}
bool dfs(int x, int col) {
c[x] = col;
bool ok = true;
sumc[col] += sum[x];
for (auto &y : linker[x]) {
if (c[y] == col) ok = false;
if (!c[y] && !dfs(y, 3 - col)) ok = false;
}
return ok;
}
int main() {
int T = read();
while (T --) {
n = read(); m = read();
for (int i = 1; i <= n; ++ i) sum[i] = sumc[i] = c[i] = 0, linker[i].clear(), fa[i] = i;
idx = 0;
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 <= m; ++ i) {
int t = read(), x = read(), y = read();
if (t == 2) merge(x, y);
else optb[++ idx] = { x, y };
}
for (int x = 1; x <= n; ++ x) {
sum[find(x)] += b[x] - a[x];
}
for (int i = 1; i <= idx; ++ i) {
int x = optb[i].first, y = optb[i].second;
linker[find(x)].push_back(find(y));
linker[find(y)].push_back(find(x));
}
bool flag = true;
for (int x = 1; x <= n; ++ x) {
if (!c[x] && find(x) == x) {
sumc[1] = sumc[2] = 0;
bool ok = dfs(x, 1);
if (ok) {
if (sumc[1] != sumc[2]) { flag = false; break; }
}
else {
if ((sumc[1] + sumc[2]) & 1) { flag = false; break; }
}
}
}
if (flag) puts("YES");
else puts("NO");
}
return 0;
}