• [NOI Online 提高组]序列 (并查集+二分图判定)


    [NOI Online 提高组]序列 (并查集+二分图判定)

    题面

    分析

    首先不妨令(b_i=b_i-a_i),这样我们需要通过一系列操作把所有(b_i)变成0.

    我们把每个位置看成点。
    对于所有2操作连边。如果两个位置连通,就可以使一个位置+1,一个位置-1.那么对于一个连通块,无论我们怎么操作总和都不变。因此我们用并查集将连通块缩成一个点,缩成点的权值为块内所有点的权值之和。

    再对于1操作连边。
    如果该图是二分图,那么选一条边+1,左部和右部节点的和都会+1.因此无论我们怎么操作左右部节点的差都不变. 那么如果左右部节点的权值之和不相等,那么一定无解。否则容易构造出一种方案。
    如果该图不是二分图,无论我们怎么操作总和的奇偶性不变,因为要么+2,要么-2.那么如果总和为奇数,那么一定无法变成0,无解。

    那么并查集缩点,再dfs跑二分图判定,同时统计一下节点权值和即可。

    代码

    #include<iostream>
    #include<cstdio>
    #include<vector> 
    #include<cstring>
    #define maxn 100000
    using namespace std;
    int T,n,m;
    int a[maxn+5],b[maxn+5];
    struct oper {
    	int t;
    	int u;
    	int v;
    } q[maxn+5];
    
    struct disjoint_set {
    	int fa[maxn+5];
    	int find(int x) {
    		if(fa[x]==x) return x;
    		else return fa[x]=find(fa[x]);
    	}
    	void merge(int x,int y) {
    		fa[find(x)]=find(y);
    	}
    	void ini(int n){
    		for(int i=1;i<=n;i++) fa[i]=i;
    	}
    }S;
    int sum[maxn+5];//每个连通块的权值之和 
    
    vector<int>E[maxn+5];//缩点之后的图 
    void add_edge(int u,int v){
    	E[u].push_back(v);
    	E[v].push_back(u);
    }
    int col[maxn+5];//二分图染色 
    int tot[3];//二分图两边节点的权值和 
    bool dfs(int x,int c){
    	col[x]=c;
    	tot[c]+=sum[x];
    	bool flag=1;
    	for(int i=0;i<(int)E[x].size();i++){
    		int y=E[x][i];
    		if(!col[y]){
    			if(!dfs(y,3-c)) flag=0;	//这里不能直接break,因为要遍历所有节点求和 
    		}else if(col[y]==c) flag=0;
    	}
    	return flag;
    }
    
    void clear(){
    	for(int i=1;i<=n;i++){
    		sum[i]=col[i]=0;
    		E[i].clear();
    	}
    	S.ini(n);
    }
    int main() {
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d %d",&n,&m);
    		clear();
    		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    		for(int i=1;i<=n;i++){
    			scanf("%d",&b[i]);
    			b[i]=b[i]-a[i];
    		}
    		for(int i=1;i<=m;i++) scanf("%d %d %d",&q[i].t,&q[i].u,&q[i].v);
    		for(int i=1;i<=m;i++){//按操作2连边 
    			if(q[i].t==2) S.merge(q[i].u,q[i].v);
    		}
    		for(int i=1;i<=n;i++) sum[S.find(i)]+=b[i];
    		for(int i=1;i<=m;i++){//缩点后按操作1连边 
    			if(q[i].t==1) add_edge(S.find(q[i].u),S.find(q[i].v));
    		}
    		bool flag=1;
    		for(int i=1;i<=n;i++){
    			if(S.find(i)==i){//遍历缩点后的每个节点 
    				if(col[i]) continue;
    				tot[1]=tot[2]=0;
    				bool is_bi=dfs(i,1);
    				if(is_bi&&tot[1]!=tot[2]) flag=0;
    				if(!is_bi&&(tot[1]-tot[2])&1) flag=0;
    			}
    		}
    		if(flag) puts("YES");
    		else puts("NO");
    	} 
    }
    
  • 相关阅读:
    1.Netty 实战前言
    8.Netty发送对象
    7.Netty中 handler 的执行顺序
    6.高性能NIO框架netty
    5.NIO_ Selector选择器
    4.NIO_Channel 通道
    3.NIO_Buffer缓冲区
    2.Java NIO 简介
    那些堪称神器的 Chrome 插件
    获取当前操作的IFrame 对象的方法
  • 原文地址:https://www.cnblogs.com/birchtree/p/12562474.html
Copyright © 2020-2023  润新知