Link
对于操作二,如果有三个数 a 1 ∼ a 3 a_1 \sim a_3 a1∼a3 有 ( 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,a2−1,a2+1,a3−1⇒(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,a2−1,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。
#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;
}