P6185 [NOI Online #1 提高组] 序列 题解

P6185 [NOI Online #1 提高组] 序列

题面:

题目背景
由于本题数据较难构造,所以无法保证卡掉所有错误做法。
题目描述

小 D 有一个长度为 n n n 的整数序列 a 1 … n a_{1 \dots n} a1n,她想通过若干次操作把它变成序列 b i b_i bi

小 D 有 m m m 种可选的操作,第 i i i 种操作可使用三元组 ( t i , u i , v i ) (t_i,u_i,v_i) (ti,ui,vi) 描述:若 t i = 1 t_i=1 ti=1,则她可以使 a u i a_{u_i} aui a v i a_{v_i} avi 都加一或都减一;若 t i = 2 t_i=2 ti=2,则她可以使 a u i a_{u_i} aui 减一、 a v i a_{v_i} avi 加一,或是 a u i a_{u_i} aui 加一、 a v i a_{v_i} avi 减一,因此当 u i = v i u_i=v_i ui=vi 时,这种操作相当于没有操作。

小 D 可以以任意顺序执行操作,且每种操作都可进行无限次。现在给定序列与所有操作,请你帮她判断是否存在一种方案能将 a i a_i ai 变为 b i b_i bi。题目保证两个序列长度都为 n n n。若方案存在请输出 YES,否则输出 NO

输入格式

本题输入文件包含多组数据。

第一行一个正整数 T T T 表示数据组数。对于每组数据:

第一行两个整数 n , m n,m n,m,表示序列长度与操作种数。

第二行 n n n 个整数表示序列 a i a_i ai

第三行 n n n 个整数表示序列 b i b_i bi

接下来 m m m 行每行三个整数 t i , u i , v i t_i,u_i,v_i ti,ui,vi,第 i i i 行描述操作 i i i

注意:同一个三元组 ( t i , u i , v i ) (t_i,u_i,v_i) (ti,ui,vi) 可能在输入中出现多次。

输出格式

对于每组数据输出一行一个字符串 YESNO 表示答案。

样例 #1
样例输入 #1
3
1 1
1
3
1 1 1
2 3
1 2
4 5
1 1 2
2 1 2
1 1 2
3 3
1 2 3
5 5 4
1 1 2
1 1 3
2 2 3
样例输出 #1
YES
YES
YES
提示
样例 1 解释

第一组数据:使用一次操作 1 1 1
第二组数据:使用三次操作 1 1 1
第三组数据:使用三次操作 1 1 1,令 a 1 , a 2 a_1,a_2 a1,a2 都增加 3 3 3,再使用一次操作 2 2 2,令 a 1 , a 3 a_1,a_3 a1,a3 都增加 1 1 1


数据范围与提示

对于测试点 1 ∼ 5 1 \sim 5 15 n = 2 n=2 n=2 m = 1 m=1 m=1 a i , b i ≤ 99 a_i,b_i \le 99 ai,bi99 u 1 ≠ v 1 u_1 \ne v_1 u1=v1 t 1 = 1 t_1=1 t1=1
对于测试点 6 ∼ 10 6 \sim 10 610 n = 2 n=2 n=2 m = 1 m=1 m=1 a i , b i ≤ 99 a_i,b_i \le 99 ai,bi99 u 1 ≠ v 1 u_1 \ne v_1 u1=v1 t 1 = 2 t_1=2 t1=2
对于测试点 11 ∼ 12 11 \sim 12 1112 n = 2 n=2 n=2 a i , b i ≤ 99 a_i,b_i \le 99 ai,bi99 u i ≠ v i u_i \ne v_i ui=vi
对于测试点 13 ∼ 16 13 \sim 16 1316 t i = 2 t_i=2 ti=2
对于测试点 17 17 17 n , m ≤ 20 n,m \le 20 n,m20
对于测试点 18 18 18 n , m ≤ 1 0 3 n,m \le 10^3 n,m103
对于所有测试点: 1 ≤ T ≤ 10 1 \le T \le 10 1T10 1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1n,m105 1 ≤ a i , b i ≤ 1 0 9 1 \le a_i,b_i \le 10^9 1ai,bi109 t i ∈ { 1 , 2 } t_i \in \{1,2\} ti{1,2} 1 ≤ u i , v i ≤ n 1\le u_i,v_i \le n 1ui,vin

二分图染色!NOIP2023考了

我们发现每个 opt ⁡ 2 \operatorname{opt}_2 opt2 就像流水,可以在两个点中任意流转

那么,我们先将每个 opt ⁡ 2 \operatorname{opt}_2 opt2 配合并查集连边缩点,

再记 c S = ∑ i ∈ S b i − a i c_S = \sum_{i \in S} b_i - a_i cS=iSbiai,相同的本质就是让每一个 c S = 0 c_S = 0 cS=0

然后我们考虑对 opt ⁡ 1 \operatorname{opt}_1 opt1 连边,形成一个图:

  1. 若该图是二分图,两边同加同减,只要二分图两边总和相同即可

  2. 若该图不是二分图,必然有一边内部有一条 opt ⁡ 1 \operatorname{opt}_1 opt1 的边
    つまり,有一边或两边总和都可以进行 + 2 +2 +2 − 2 -2 2 的操作,
    而这种操作不会改变两边的奇偶性!故判断两边总和奇偶性是否相同!

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
constexpr int N = 2e5+5;
int n,m,a[N],b[N],d[N],s[N],co[N],p[N],top;
array<int,3> q[N];
int find(int x) {
	if(s[x] ^ x) s[x] = find(s[x]);
	return s[x];
}

int head[N],nxt[N << 1],to[N<<1],cnt;
void init() {
	for(int i = 0;i<=n;i++) head[i] = -1;
	for(int i = 0;i<=n;i++) d[i] = 0,s[i] = i,co[i] = 0;
	cnt = 0;top = 0;
}

void add(int u,int v) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	head[u] = cnt++;
}

bool dfs(int x, int col) {
	co[x] = col, p[col] += d[x];
	bool ok = 1;
	for (int i = head[x];~i;i = nxt[i]) {
		int y = to[i];
		if (co[y] == co[x]) ok = 0;
		if (!co[y] && !dfs(y, 3 - col)) ok = 0;
	}
	return ok;
}


bool solve(){
	n = rd(),m = rd();
	for(int i = 1;i<=n;i++) a[i] = rd();
	for(int i = 1;i<=n;i++) b[i] = rd();
	init();
	for(int i = 1;i<=m;i++) {
		array<int,3> c;
		c[0] = rd(),c[1] = rd(),c[2] = rd();
		if(c[0] == 2) {
			int fx = find(c[1]),fy = find(c[2]);
			s[fx] = fy;
		}else q[++top] = c;
	}
	for(int i = 1;i<=n;i++) d[find(i)] += b[i] - a[i];
	for(int i = 1;i<=top;i++) {
		int fx = find(q[i][1]),fy = find(q[i][2]);
		add(fx,fy);add(fy,fx);
	}
	for(int i = 1;i<=n;i++) 
		if(find(i) == i && !co[i]) {
			p[1] = p[2] = 0;
			bool ok = dfs(i,1);
			if(ok && p[1] != p[2]) return false;
			if(!ok && ((p[1] ^ p[2]) & 1)) return false;
		}
	return true;

}

signed main() {
	int T = rd();
	while(T--) puts(solve() ? "YES" : "NO");
	return 0;
}
  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值