2-SAT备忘(codeforces 1791H)

例题链接:cf1971H

2-SAT本质上是将一些01变量间的限制关系以图的形式表现出来。

对于一个变量 a i a_i ai,我们会建两个点分别表示 a i = 1 a_i = 1 ai=1 a i = 0 a_i = 0 ai=0
然后我们定义一条限制 l i m ( a i , a j , x , y ) lim(a_i,a_j,x,y) lim(ai,aj,x,y) a i = x a_i =x ai=x a j = y a_j = y aj=y不能同时出现。则对于一条限制,我们会连两条边: a i = x a_i=x ai=x to a j = y  xor  1 a_j=y \text{ xor } 1 aj=y xor 1 a j = y a_j = y aj=y to a i = x  xor  1 a_i = x \text{ xor } 1 ai=x xor 1。 表示如果我们选了 a i = x a_i = x ai=x,就只能再选 a j = y  xor  1 a_j = y \text{ xor } 1 aj=y xor 1。另一条边类似。

然后判定可行解只需要对全图跑SCC,如果 a i = 0 a_i = 0 ai=0 a i = 1 a_i = 1 ai=1在同一个SCC里则不可行。感性理解一下就是我们选了 a i = 0 a_i = 0 ai=0并且在一长串路径后得到我们还得选 a i = 1 a_i = 1 ai=1,就矛盾了。

例题解法:每一列的三个式子中里至少要有两个为1。可以看成任意两个式子里要有一个为1,所以就是任意两个式子不能为0。一列等价于3条限制。建完图之后直接跑2-SAT就行。

#include<bits/stdc++.h>
using namespace std;
struct strongly_connected{
	vector<vector<int>> gph;
	void init(int n){
		gph.clear();
		gph.resize(n);
	}
	void add_edge(int s,int e){
		gph[s].push_back(e);
	}
	vector<int> val,comp,z,cont;
	int Time,ncomps;
	template<class G,class F> int dfs(int j, G&g, F f){
		int low=val[j]=++Time,x;z.push_back(j);
		for(auto e:g[j])if(comp[e]<0)
			low=min(low, val[e]?:dfs(e,g,f));
		if(low==val[j]){
			do{
				x=z.back();
				z.pop_back();
				comp[x]=ncomps;
				cont.push_back(x);
			}while(x!=j);
			f(cont);cont.clear();
			ncomps++;
		}
		return val[j]=low;
	}
	template<class G,class F>void scc(G& g, F f){
		int n=g.size();
		val.assign(n,0);comp.assign(n,-1);
		Time=ncomps=0;
		for(int i=0;i<n;i++)if(comp[i]<0)dfs(i,g,f);
	}
	int piv;
	void get_scc(int n){
		scc(gph,[&](vector<int> &v){});
		for(int i=0;i<n;i++){
			comp[i]=ncomps-comp[i];
		}
		piv=ncomps;
	}
}scc;
struct twosat{
	strongly_connected scc;
	int n;
	void init(int _n){scc.init(2*_n);n=_n;}
	int NOT(int x){return x>=n?(x-n):(x+n);}
	void add_edge(int x,int y){
		if(x<0)x = (-x)+n;
		if(y<0)y = (-y)+n;
		scc.add_edge(x,y);scc.add_edge(NOT(y),NOT(x));
	}
	bool satisfy(int *res){//res存的是一组合法解
		scc.get_scc(2*n);
		for(int i=0;i<n;i++){
			if(scc.comp[i]==scc.comp[NOT(i)]) {
				return 0;
			}
			if(scc.comp[i]<scc.comp[NOT(i)])res[i]=0;
			else res[i]=1;
		}
		return 1;
	}
}twosat;
const int maxn=505;
int t,n,a[3][maxn],res[maxn];

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		twosat.init(n+1);//因为题目输入从1开始,但是我2-SAT的模板下标从0开始。
		for(int i=0;i<3;i++){
			for(int j=1;j<=n;j++)cin>>a[i][j];
		}
		for(int j=1;j<=n;j++){
			for(int i=0;i<3;i++){
				int nxt=(i+1)%3;
				twosat.add_edge(-a[i][j],a[nxt][j]);
			}
		}
		if(twosat.satisfy(res))puts("YES");
		else puts("NO");
	}
	return 0;
}

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值