2-SAT

具体细节可见 this
\(2013\) 年的远古博客,写的还是很好的
只是由于时间原因,其中的一些算法显得有些过时了。。。
本文主要总结步骤和实现
首先,原文中的步骤大体为

  1. 建图
  2. 缩点
  3. 拓扑+染色

这种思路毫无疑问是没有问题的
我用这种思路写着了这道水题 P5782

#include<bits/stdc++.h>
using namespace std;
namespace Basic{
	const int N=16009,M=40009;
	int n,m,color[N],cnt,In[N],Rev[N];
	struct line{int l,r;}tmp[M];
	vector<int> k[N];
}
namespace Build{
	using namespace Basic;
	inline int f(int u){
		if(u&1)return u+1;
		return u-1;
	}
	void Scan(){
		int l,r;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)scanf("%d%d",&l,&r),k[f(r)].push_back(l),k[f(l)].push_back(r),tmp[++cnt]={f(r),l},tmp[++cnt]={f(l),r}/*,printf("Link %d %d\nLink %d %d\n",f(r),l,f(l),r)*/;
	}
}
namespace Tarjan{
	using namespace Basic;
	int sccnt=0,idx=0,dfn[N],low[N],top;
	bool flag[N];
	stack<int> st;
	void Tarjan(int i=1){
		dfn[i]=low[i]=++idx,flag[i]=1,st.push(i);
		for(int j:k[i]){
			if(!dfn[j]) Tarjan(j),low[i]=min(low[i],low[j]);
			else if(flag[j]) low[i]=min(low[i],low[j]);
		}
		if(dfn[i]!=low[i])return;
		++sccnt;
		while(true){
			/*printf("color of %d is %d!\n",st.top(),sccnt);*/
			top=st.top(),color[top]=sccnt,st.pop(),flag[top]=0;
			if(top==i)return;
		}
	}
	void Rebuild(){
		for(int i=1;i<=n+n;i++)k[i].clear();
		for(int i=1;i<=n;i++){
			Rev[color[i*2-1]]=color[i*2];
			Rev[color[i*2]]=color[i*2-1];
		}
		for(int i=1;i<=cnt;i++) if(color[tmp[i].l]!=color[tmp[i].r])k[color[tmp[i].l]].push_back(color[tmp[i].r]),++In[color[tmp[i].r]]/*,printf("Rebuild %d->%d\n",color[tmp[i].l],color[tmp[i].r])*/;
	}
	void Solve(){
		for(int i=1;i<=n+n;i++)if(!dfn[i])Tarjan(i);
		Rebuild();
	}
}
namespace Sort{
	using namespace Basic;
	using Tarjan::sccnt;
	bool killed[N],vis[N];
	int kth[N],idx;
	vector<int> ans;
	void Check(){for(int i=1;i<=sccnt;i++) if(Rev[i]==i) puts("NIE"),exit(0);}
	void Kill(int i){
		if(killed[i])return;
		killed[i]=1;
		for(int j:k[i])Kill(j);
	}
	void Sort(int i){
		kth[++idx]=i,vis[i]=1;
		for(int j:k[i]) if(--In[j]==0) Sort(j);
	}
	void Solve(){
		Check();
		for(int i=1;i<=sccnt;i++)if(!vis[i]&&In[i]==0)Sort(i);
		for(int o=1,i;o<=sccnt;o++){
			i=kth[o];
			if(!killed[i]){
				for(int it=1;it<=n+n;it++) if(color[it]==i)ans.push_back(it);
				Kill(Rev[i]);
			}
		} 
		sort(ans.begin(),ans.end());
		for(int i:ans)printf("%d\n",i);
	}
}
int main(){
	Build::Scan();
	Tarjan::Solve();
	Sort::Solve();
	return 0;
}

交上去是能 \(\text{AC}\)
但有一个很显然的问题就是码量较大
润题解发现 \(dalao\) 们都不用拓扑缩点染色等等
一个乱搞比较,直接就过了

为啥子呢?
我们上面的代码其实偷了懒
正常情况下我们应该先缩点再搞反图
但是我们因为 \(\text{Tarjan}\) 的唯一作用就是进行缩点
而强联通分量的形态数量和边的方向没有关系
所以可以这么写

如果我们正常缩完点再建反图的话
你会发现我们的 \(\text{Tarjan}\) \(sccnt\) 序和拓扑序有一种玄学的相反关系
(拓扑是由浅入深,\(\text{Tarjan}\) 是类似 DFS 回溯的的由深回溯到浅)
但由于我们是反图拓扑,所以负负得正
\(sccnt\) 其实就是反图拓扑序!
所以我们其实可以将 \(sccnt\) 当拓扑序进行染色

后面就很玄学了
我们盲猜拓扑序小的取真
发现这样可以
大概就是反图中一个点只会禁掉 \(sccnt\) 比它大的点
所以拓扑序小的相对有优势

以上纯属口胡
就这样吧

Code P7482

#include<bits/stdc++.h>
using namespace std;
const int N=2000003;
int n,m,idx,sccnt,dfn[N],low[N],color[N];
bool vis[N];
vector<int> k[N];
stack<int> st;
void Tarjan(int i){
	dfn[i]=low[i]=++idx,vis[i]=1,st.push(i);
	for(int j:k[i]) if(!dfn[j]) Tarjan(j),low[i]=min(low[i],low[j]); else if(vis[j]) low[i]=min(low[i],low[j]);
	if(dfn[i]!=low[i])return;
	int top; ++sccnt;
	while(1){
		top=st.top(),st.pop(),vis[top]=0,color[top]=sccnt;
		if(top==i)return;
	}
}
int main(){
	int l,r,a,b;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%d",&l,&a,&r,&b);
		k[(l<<1)-(a^1)].push_back((r<<1)-b),k[(r<<1)-(b^1)].push_back((l<<1)-a);
	}
	for(int i=1;i<=n+n;i++)if(!dfn[i])Tarjan(i);	
	for(int i=1;i<=n+n;i+=2) if(color[i]==color[i+1])puts("IMPOSSIBLE"),exit(0); 
	puts("POSSIBLE");
	for(int i=2;i<=n+n;i+=2)printf("%d ",color[i]>color[i-1]);
	return 0;
}

Accoders 弱化和平委员会

#include<bits/stdc++.h>
using namespace std;
const int N=16003;
vector<int> k[N];
stack<int> st;
int dfn[N],low[N],idx,sccnt,color[N],tmp;
bool vis[N];
void dfs(int i){
    dfn[i]=low[i]=++idx,vis[i]=1,st.push(i);
    for(int j:k[i]){
        if(!dfn[j])dfs(j),low[i]=min(low[i],low[j]);
        else if(vis[j]) low[i]=min(low[i],low[j]);
    }
    if(low[i]==dfn[i]){
        ++sccnt;
        while(st.top()!=i) color[st.top()]=sccnt,vis[st.top()]=0,st.pop();
        color[st.top()]=sccnt,vis[st.top()]=0,st.pop();
    }
}
int main(){
    int n,m,l,r;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),--l,--r,k[l].push_back(r^1),k[r].push_back(l^1);
    for(int i=0;i<n+n;i++) if(!dfn[i]) dfs(i);
    for(int i=1;i<=n;i++) if(color[(i<<1)-2]==color[(i<<1)-1])puts("NO"),exit(0);
    puts("YES");
    return 0;
}
posted @ 2025-05-05 10:25  2025ing  阅读(10)  评论(0)    收藏  举报