#二分图,并查集#洛谷 6185 [NOI Online #1 提高组] 序列


题目


分析

考虑2操作可以在保证总和不变的情况下任意修改,

如果将2操作所在的连通块用并查集缩点,那么再考虑1操作,

按照1操作建边,如果存在奇环,那么只要这个环的点权和为偶数一定能使 \(a,b\) 相同;

否则一定是一张二分图,黑白染色之后黑点和白点的点权和必须相等。


代码

#include 
#include 
using namespace std;
const int N=100011; long long s[2],b[N];
struct node{int y,next;}e[N<<1];
int v[N],a[N],f[N],X[N],Y[N],n,m,flag,et,as[N];
int iut(){
	int ans=0,f=1; char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans*f;
}
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
bool dfs(int x){
	bool flag=1; s[v[x]]+=b[x];
	for (int i=as[x];i;i=e[i].next)
	if (v[e[i].y]==v[x]) flag=0;
	    else if (v[e[i].y]==-1) v[e[i].y]=v[x]^1,flag&=dfs(e[i].y);
	return flag;	
}
int main(){
	for (int T=iut();T;--T){
		n=iut(),m=iut(),X[0]=0,flag=et=1;
		for (int i=1;i<=n;++i) a[i]=iut(),f[i]=i,v[i]=-1;
		for (int i=1;i<=n;++i) a[i]=iut()-a[i],b[i]=0;
		for (int i=1;i<=m;++i){
			int opt=iut(),x=iut(),y=iut();
			if (opt==2) f[getf(x)]=getf(y);
			    else X[++X[0]]=x,Y[X[0]]=y;
		}
		for (int i=1;i<=n;++i) b[getf(i)]+=a[i];
		for (int i=1;i<=X[0];++i){
			int x=getf(X[i]),y=getf(Y[i]);
			e[++et]=(node){y,as[x]},as[x]=et;
			e[++et]=(node){x,as[y]},as[y]=et;
		}
		for (int i=1;i<=n;++i)
		if (getf(i)==i&&v[i]==-1){
			v[i]=s[0]=s[1]=0;
			bool now=dfs(i);
			if (now&&(s[0]!=s[1])) {puts("NO"),flag=0; break;}
			if (!now&&((s[0]^s[1])&1)) {puts("NO"),flag=0; break;}
		}
		for (int i=1;i<=n;++i) as[i]=0;
		if (flag) puts("YES");
	}
    return 0;
} 

相关