2-SAT
具体细节可见 this
\(2013\) 年的远古博客,写的还是很好的
只是由于时间原因,其中的一些算法显得有些过时了。。。
本文主要总结步骤和实现
首先,原文中的步骤大体为
- 建图
- 缩点
- 拓扑+染色
这种思路毫无疑问是没有问题的
我用这种思路写着了这道水题 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;
}
#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;
}

浙公网安备 33010602011771号