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 <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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为给选手提供一个锻炼的机会,CCF拟举办一场NOI Online能力测试。测试分为入门组和提高组,每组限额报名3万人,人数报满关闭系统,每组成功报名选手在同一时间参加线上测试。 一、测试时间: 提高组:3月7日8:30-12:00 入门组:3月7日14:30-18:00 二、测试方式: 1.选手报名成功后,根据系统生成的准考证号和密码登陆测试系统。 2.测试试题均采用电子版,每组3道题目。选手在线提交源程序,测试系统离线统一评测。 三、报名资格及时间: 1.因本次测试为限额参加,为给更多选手机会,每位选手限报名一组测试。如每组人数报满,系统会提前关闭。曾在CSP-J/S2019第二轮获一级的选手具有优先权。 2.分阶段报名: 第一阶段为CSP-J/S2019第二轮一级选手报名,时间:即日起至2月20日24:00截止; 第二阶段为开放报名,时间:2月21日-26日24:00截止。 四、报名方式: 1.测试费用:免费 2.报名网址:http://rg.noi.cn/(点击下载使用说明) 3.参加者请如实填写个人信息报名,包括但不限于姓名、身份证号、出生日期、性别、就学(学籍学校)/就职单位等。 4.测试前公布选手对应的动态地址,请关注邮件及NOI网站通知。 五、评测及成绩 本次测试不评奖、不颁发证书。3月20日前公示测试成绩前25%名单 六、咨询邮箱:noi@ccf.org.cn 此通知。 中国计算机学会 2020年2月17日

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值