[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;
}