题意:
给出一个 n n n个点, m m m条边的无向连通图,每个点都有一个初始点权 v i v_i vi, 还有一个最终点权 t i t_i ti ,每次操作可以选一条边,然后将这条边上的两个点同时加k或减 k k k, k k k可以任意选择,求能否将 v i v_i vi变为 t i t_i ti。
题解:
先将 t i − v i t_i-v_i ti−vi 当做每个点的点权。
首先,相邻点之间可以同时加 k k k, 可以推出相差偶数条边的两个点可以一个加 k k k,一个减 k k k 。假设1与2相连,2与3相连
那么 1 1 1和 2 2 2 可以先同时加 k k k,然后 2 2 2和 3 3 3再同时减 k k k ,最后就变成 v 1 + k , v 3 − k v_1+k,v_3-k v1+k,v3−k 。
根据上述结论,可以推出以下两个情况:
1. 1. 1.图中没有奇环的时候,两个点满足上述结论,我们把相差偶数条边的点放在一起,那么可以分为两个集合,对集合内的点操作,点权总和不变,对两个集合的点操作,两个总和同时加k,若要同时变到 0 0 0,则说明两个集合的点权总和必须要相等。
2. 2. 2.若图中存在奇环,那么可以发现,其实任意两个点既可以同时加 k k k,又可以一个加 k k k另一个减 k k k ,那么问题就变成:一个数组,可以任意选取两个元素同时加或一加一减,求能否变到 0 0 0。很明显,一加一减操作并不会改变点权总和。那么同时加k要能变到 0 0 0,意味着点权总和必须为偶数,因为每次加了两个 k k k。
还有最后一个难点,怎么求出相差偶数条边的点的集合,并且判断奇环呢?答案就是二分图染色,相差偶数条边的点的颜色一定相同,且能在染色过程中判断有无奇环。
代码:
#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=2e5+5;
const int inf=0x3f3f3f3f;
int head[MAXN];
int cnt=0;
ll v[MAXN];
ll sum1=0,sum2=0;
int color[MAXN];
struct node
{
int to;
int next;
}e[MAXN<<1];
void add(int u,int v){
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
bool bfs()
{
queue<int>q;
color[1]=1;
q.push(1);
while(!q.empty())
{
int now=q.front();
q.pop();
if(color[now]==1) sum1+=v[now];
else sum2+=v[now];
for(int i=head[now];i!=-1;i=e[i].next)
{
int to=e[i].to;
if(color[to]==-1)
{
color[to]=(color[now]^1);
q.push(to);
}
else
{
if(color[to]==color[now])
{
return false;
}
}
}
}
return true;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
color[i]=-1;
scanf("%lld",&v[i]);
}
ll sum=0;
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
v[i]=x-v[i];
sum=sum+v[i];
}
for(int i=1;i<=n;i++){
head[i]=-1;
}
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
sum1=sum2=0;
bool flag=bfs();
if(flag){
if(sum1==sum2){
printf("YES\n");
}else{
printf("NO\n");
}
}
else{
if(abs(sum)%2==0){
printf("YES\n");
}else{
printf("NO\n");
}
}
}
}