不错的题8~~(不知为啥到我手上特判的贼多)~~c++
简述题意:给你$n$个结点,每一个结点有一个初始值$a_i$,以及目标值,而且给定两种边$(u_i,v_i)$,第一种边使$a_{u_i}$和$a_{v_i}$同时加一,第二种边使$a_{u_i}$和$a_{v_i}$一个加一,一个减一。问最后可否使全部结点变成目标状态。git
如下是我在当时测试时的思路。测试
首先到达最终的目标,也就是说使得对应的结点的数值增长$b_i-a_i$,让每一个结点表示这个值,问题就转化成了可否使全部的数值变成$0$了。spa
看两种边有什么特别之处。一会儿看第一种边没发现什么,第二种边至关于能够将一个结点上的部分数字“传输”到与其相邻的结点之处,稍加拓展不难发现第二种边构成的连通图中,任意一个结点可将其数字传到另外的任意一个结点,以下图:code
这样的意义在于:能自由分配连通图中全部的数值(都转移到一个结点、或者分配到若干个点),能够无需关心具体的分配状况,因而将他们缩成一个点,其权值为$\sum w_i$。blog
这样就只剩下第一种边了。它们只有两种状况:(1)链接两个连通块(即缩点后链接两个点),这个的影响是让链接的两个缩点加或减相等的数字;(2)在某个连通块内,这个的影响是让这个缩点加或减一个偶数。咱们仍是用传输的思想,手算会发现(1)能够将某个点的数值隔两个点传过去,以下图:get
对于缩过点的图,每一个由第一种边构成的连通块,仔细想一想会发现:若是这个连通块不能被黑白染色,则必定能够将数值放到某一个奇环上的一个点,一样这个也是可逆的,故一个数必定能够借助奇环把另外一个相等的数消除;反之,若是能被黑白染色,显然能够将全部黑点的数值和白点的数值移到一条相邻的边,而后再一块儿消掉。没有(2)状况的干扰下,对于每一个连通块,按照上面分类讨论,若是仍然不能彻底消除,那显然输出$\text{NO}$了。全部的都知足才输出$\text{YES}$。it
加上(2),其实就是能够对权值调整,在有(2)的连通块下,不必定要所有消除,只要消到能剩余$2$的倍数就好啦(能够依靠这样的边在连通块内部就消除掉)。因而咱们整理一下:class
一、根据第二种边求出全部的连通块而且缩起来;im
二、根据第一种边求出全部的连通块,对每一个连通块判断可否黑白染色,若是能就染色,求出全部黑点和白点的权值和,顺便看看有没有状况(2);
三、检查该连通块是否知足要求:a.若是不能黑白染色,只须要看这个连通块的权值和是否是偶数;b.若是能,判断黑点权值和和白点权值和的关系,若是有(2),那么只需差为偶数便可;不然必须相等(具体详见上文);
四、若是全部的连通块都能经过测试,输出$\text{YES}$;不然输出$\text{NO}$。
时间复杂度$\text{O}(Tn)$。能够经过。
#include <bits/stdc++.h> #define ll long long #define ull unsigned long long #define min(a, b) (a) < (b) ? (a) : (b) #define max(a, b) (a) > (b) ? (a) : (b) #define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i) #define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i) #define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i) #define per0(i, a) for (int i = a-1; ~i; --i) #define chkmax(a, b) a = max(a, b) #define chkmin(a, b) a = min(a, b) inline int read() { int w = 0, f = 1; char c; while (!isdigit(c = getchar())) c == '-' && (f = 1); while (isdigit(c)) w = w*10+(c^48), c = getchar(); return w * f; } const int maxn = 114514; int n, m; std::vector<int> G1[maxn], G2[maxn], G[maxn]; int id[maxn], delta[maxn], tag[maxn], col[maxn], idcnt = 0; // id为连通块编号、delta表示a[i]-b[i](相反亦可),tag表示有没有(2)状况,col为染色状况,idcnt表示连通块个数 ll sum[maxn]; // 每一个连通块内的权值总和 void dfs(int u) { sum[id[u] = idcnt] += delta[u]; // 标记连通块而且合并权值 rep0(i, G2[u].size()) if (!id[G2[u][i]]) dfs(G2[u][i]); } int paint(int u, ll &white, ll &black, int &flag) { int ok = 1; // 表示是否染色成功 col[u] == 1 ? white += sum[u] : black += sum[u]; flag |= tag[u]; // 计算white,black点的权值 rep0(i, G[u].size()) { int v = G[u][i]; if (col[v]) { if (col[v] == col[u]) ok = 0; continue; } // 失败 col[v] = 3-col[u]; ok &= paint(v, white, black, flag); } return ok; } int main() { for (int T = read(); T; T--) { n = read(), m = read(); rep(i, 1, n) delta[i] = read(); rep(i, 1, n) delta[i] -= read(); rep(i, 1, n) G1[i].clear(), G2[i].clear(); // 清空 rep(i, 1, m) { int t = read(), u = read(), v = read(); if (t == 1) G1[u].push_back(v), G1[v].push_back(u); if (t == 2) G2[u].push_back(v), G2[v].push_back(u); } memset(id, 0, sizeof id); idcnt = 0; rep(i, 1, n) if (!id[i]) G[++idcnt].clear(), sum[idcnt] = tag[idcnt] = col[idcnt] = 0, dfs(i); // 初始化+标记 rep(u, 1, n) rep0(i, G1[u].size()) { int v = G1[u][i]; if (id[u] == id[v]) tag[id[u]] = 1; else G[id[u]].push_back(id[v]); } int ans = 1; rep(i, 1, idcnt) { if (col[i]) continue; // 染过色直接跳过 ll white = 0, black = 0; int flag = 0; col[i] = 1; if (paint(i, white, black, flag)) { // 染色成功 if (flag) { if ((white ^ black) & 1) { ans = 0; break; } } // 有(2)状况判断差是否为偶数 else if (white != black) { ans = 0; break; } // 没有就判断是否相等 } else if ((white ^ black) & 1) { ans = 0; break; } // 不成功判断差是否为偶数 } printf("%s\n", ans ? "YES" : "NO"); } return 0; }