[复习] 种类并查集 & 2-XOR-SAT

[复习] 种类并查集

种类并查集也可叫做扩展域并查集。

前言

自从两年多前刚学并查集时过了食物链后,就再也没有写过种类并查集。
今天回顾一下。

例题 1 食物链

P2024 [NOI2001] 食物链

题目大意:有 \(n\) 个动物,每个动物属于 \(A,B,C\) 种中的一种,\(A\)\(B\)\(B\)\(C\)\(C\)\(A\)。每次给出 \(x,y\) 同类或 \(x\)\(y\) 的信息,看是否合法。

首先 \(x,y\) 同类可以想到用并查集维护。那么 \(x\)\(y\) 怎么维护呢。

\(x\)\(y\) 时,我们不能将 \(x,y\) 放到一个并查集里表示它们不同。否则如果 \(a\to b,b\to c,c\to d\),那此时 \(a,d\) 其实是同类的。

考虑一个动物,我们并不需实际区分它是哪个类别,只需考虑它和其他动物的关系即可。

于是考虑并查集取出 \(3n\) 个点,\(1\sim n\) 为第一类,\(n+1\sim 2n\) 为第二类,\(2n+1\sim 3n\) 为第三类。

我们这样表示它们的关系:

对于同一类中的点 \((x,y)\),如果它们在同一个集合中,则 \((x,y)\) 同类。
对于不在同一类中的点 \((x+kn,y+k'n)\),如果它们在同一个集合中,则 \((x,y)\) 不同类。
第一类吃第二类,第二类吃第三类,第三类吃第一类。

于是缩点就变成了:

  • 如果 \(x,y\) 同类,那么 \((x,y),(x+n,y+n),(x+2n,y+2n)\) 缩点。
  • 如果 \(x\to y\),那么 \((x,y+n),(x+n,y+2n),(x+2n,y)\) 缩点。

2-XOR-SAT

SAT 问题,是一个 NP 完全问题,求 \(n\) 个布尔变量,有若干条限制,每条限制形如给定若干布尔变量分别为 \(0/1\),要至少有一个布尔变量满足。

  • 2-SAT 问题,每条限制由两个变量组成,可以用 SCC 缩点在 \(O(n+m)\) 解决。
  • XOR-SAT 问题,将 SAT 问题的限制改为用异或连接,每条限制要使最终异或值为 \(0/1\),可以用高斯消元在 \(O(n^3)\) 解决。
  • 2-XOR-SAT 问题,每条限制给定两个变量和它们的异或值,要求构造方案或判断无解。

2-XOR-SAT 问题可用本篇的种类并查集做。

还是 \(1\sim n\) 表示第一类,\(n+1\sim 2n\) 表示第二类,同类点缩在一起表示它们相等,不同类点缩在一起表示它们不等。

我们知道,异或值指示了两个布尔数是否相同。

于是显然有这样的缩点策略:

  • 如果 \(a_x\oplus a_y=1\),那么缩点 \((x,y+n),(x+n,y)\) 表示 \((x,y)\) 不等。
  • 如果 \(a_x\oplus a_y=0\),那么缩点 \((x,y),(x+n,y+n)\) 表示 \((x,y)\) 相等。

例题 2 arc183_c

arc183_c

题目大意:
\(n\) 个人,每个人是诚实或撒谎,清醒或糊涂。其中:
清醒的诚实人说真话。
清醒的撒谎人说假话。
糊涂的诚实人说假话。
糊涂的撒谎人说真话。
给定 \(m\) 个条件,形如 \(a,b\in [1,n],c=0/1\),表示 \(a\) 号人说 \(b\) 号人是诚实或撒谎。

我们可以设布尔变量 \(p_i\)\(0/1\) 表示 \(i\) 是诚实或是撒谎,\(q_i\)\(0/1\) 表示 \(i\) 是清醒或是糊涂。

那么条件 \((a,b,c)\) 就相当于要满足 \(p_a\oplus q_a\oplus p_b=c\)

这是三元的,并不好,我们可以考虑设 \(r_i=p_i\oplus q_i\),那么条件就变成了 \(r_a\oplus p_b=c\),我们可以求 \(r_i,p_i\),然后用 \(q_i=r_i\oplus p_i\) 算出 \(q_i\)

关于方案,如果最后合法了,那么对于缩在一起的点(包括不同类点),我们指定一个点随便取一个值后,就能全部确定这些点。

AC 代码:

const int N=2e5+5;
int n,m;
int fa[N*4];
int getfa(int x){
	if(fa[x]==x)return x;
	return fa[x]=getfa(fa[x]);
}
void merge(int x,int y){
	if(getfa(x)!=getfa(y))
	fa[getfa(x)]=getfa(y);
}
int nx(int x){
	if(x<=2*n)return x+2*n;
	return x-2*n;
}
int r(int x){
	return x;
}
int p(int x){
	return x+n;
}
void no(){
	write("-1");
	exit(0);
}
vector<int> t[N*4];
int ans[N*4],vis[N*4];
signed main(){
	read(n,m);
	fo(i,1,n*4)fa[i]=i;
	fo(i,1,m){
		int a,b,c;
		read(a,b,c);
		if(c){
			if(getfa(r(a))==getfa(p(b))){
				no();
			}
			merge(r(a),nx(p(b)));
			merge(nx(r(a)),p(b));
		}
		else{
			if(getfa(r(a))==getfa(nx(p(b)))){
				no();
			}
			merge(r(a),p(b));
			merge(nx(r(a)),nx(p(b)));
		}
	}
	fo(i,1,n*2)if(getfa(i)==getfa(nx(i)))no();
	fo(i,1,n*4)t[getfa(i)].push_back(i);
	fo(i,1,n*4){
		for(auto j:t[i]){
			if(j<=2*n&&!vis[j]){
				vis[j]=1;
				ans[j]=0;
			}
			else if(j>2*n&&!vis[j-2*n]){
				vis[j-2*n]=1;
				ans[j-2*n]=1;
			}
		}
	}
	fo(i,1,n)write(ans[i]^ans[i+n]);
	return 0;
}
posted @ 2024-11-26 21:19  dengchengyu  阅读(49)  评论(0编辑  收藏  举报