10086

my first title

my first paragraph

[ARC188C] Honest or Liar or Confused

扩展域并查集+带权并查集

题意中给的是骗子与否和糊涂与否,似乎有多个二元关系。观察结果:如果一个人不糊涂,那么 \(C = 0\) 代表他们同是诚实的或者都是骗子;\(C = 1\) 代表他们的诚实与否不同。

这时我们就可以不在意这个人是否诚实了,我们就去关系人与人之间的相对关系。若这个人是糊涂的,事实上的 \(C\) 就会相反。那么“处理关系”,“关系相反”,想到使用带权并查集,权值为 01 异或。那么如何处理糊涂与否呢?我们可以利用对带权并查集的结果去判断。具体的,每个点 \(U\) 都设置一个扩展点 \(V\),表示这个人所表述的。每次处理 \(a,b,c\),就将 \(V_a\)\(U_b\) 合并,边权为 \(c\)。后面遍历下来对 \(U,V\) 处理,根据并查集中的权值即可判定 \(U_i,V_i\) 之间的边权,若为 \(1\),则这个人是糊涂的。

增加 \(V\) 点作为扩展域并查集作用的一个体现,即将这个人所说的东西与这个人独立出来,判断是否自洽。再在后面对这个人本身和这个人所说的相关联,判断是否愚蠢,达成目的。

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10;
int n,m;
struct DSU{
	int fa[N],val[N];
	inline void init(int _n){
		for(int i = 1;i<=_n;++i)fa[i] = i, val[i] = 0;
	}
	int find(int x){
		if(x == fa[x])return x;
		int pa = find(fa[x]);
		val[x] ^= val[fa[x]];
		return fa[x] = pa;
	}
	inline bool merge(int x,int y,int d){
		int fx = find(x), fy = find(y);
		if(fx == fy)return val[x] ^ d == val[y];
		fa[fx] = fy;
		val[fx] = val[x] ^ val[y] ^ d;
		return true;
	}
	int & operator [](const int i){ return val[i]; }
}dsu;

int main(){
	scanf("%d%d",&n,&m);
	dsu.init(n << 1);
	int a,b,c;
	while(m--){
		scanf("%d%d%d",&a,&b,&c);
		if(!dsu.merge(a+n,b,c))return puts("-1"),0;
	}
	for(int i = 1;i<=n;++i){
		if(dsu.find(i) == dsu.find(i+n)){
			(dsu[i] ^ dsu[i+n]) ? putchar('1') : putchar('0');
		}else{
			dsu.merge(i,i+n,0);
			putchar('0');
		}
	}
	return 0;
}
posted @ 2024-11-24 10:05  Luzexxi  阅读(16)  评论(0编辑  收藏  举报