2-SAT:
好像大概是这么一个东西,有一些集合,每个集合中有两个元素
(ai,bi)
(
a
i
,
b
i
)
,然后要求你从每一个集合中选出一个元素,但是同时对于集合元素的选取是有一些限制的,比如说什么选了
ai
a
i
和
aj
a
j
必须选一个,或者选了
ai
a
i
就必须选
bj
b
j
之类的。
然后我们发现上面所有的限制条件都可以转化为一类,即选了
ai
a
i
就必须选择
bj
b
j
这种。
然后解决这种问题算法就是建立一个有向图,对于选
ai
a
i
就必须选
bj
b
j
这种限制,我们在图中从
ai
a
i
连向
bj
b
j
,同时我们发现如果选
aj
a
j
也必须选
bi
b
i
,所以在图中从
aj
a
j
连向
bi
b
i
。不难发现这个图有对称性。
对于这个有向图,如果从
ai
a
i
可以到达
bi
b
i
,那么
ai
a
i
一定是不可以选的,那么根据有向图的传递性以及这个图本身的对称性,那么我们发现只有当
ai
a
i
和
bi
b
i
在同一强连通分量中是才有可能是无解的。
于是我们可以把图建出来之后缩强连通分量,然后判断一下有没有无解的情况,如果有解的话就直接按照拓扑序的逆序选择每个集合内的元素。
洛谷模板:
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("luogu4782.in","r",stdin);
freopen("luogu4782.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(mul=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e6+10;
int n,m;
int beg[maxn<<1],to[maxn<<1],las[maxn<<1],cnte=1;
int bel[maxn<<1],cnt_scc,low[maxn<<1],dfn[maxn<<1],cnt_dfn;
stack<int>stk;
void add(int u,int v){las[++cnte]=beg[u];beg[u]=cnte;to[cnte]=v;}
void tarjan(int u){
low[u]=dfn[u]=++cnt_dfn;
stk.push(u);
for(int i=beg[u];i;i=las[i]){
if(!dfn[to[i]]){
tarjan(to[i]);
low[u]=min(low[u],low[to[i]]);
}
else if(!bel[to[i]])low[u]=min(low[u],dfn[to[i]]);
}
if(dfn[u]==low[u]){
++cnt_scc;
for(int p;p!=u;stk.pop()){
p=stk.top();
bel[p]=cnt_scc;
}
}
}
void init(){
read(n); read(m);
int x,y,t1,t2;
REP(i,1,m){
read(x); read(t1); read(y); read(t2);
add(x+(1-t1)*n,y+t2*n);
add(y+(1-t2)*n,x+t1*n);
}
}
void work(){
REP(i,1,(n<<1))if(!dfn[i])
tarjan(i);
REP(i,1,n)if(bel[i]==bel[i+n]){
puts("IMPOSSIBLE");
return;
}
puts("POSSIBLE");
REP(i,1,n){
if(bel[i]<bel[i+n])putchar('0');
else putchar('1');
putchar(' ');
}
}
int main(){
File();
init();
work();
return 0;
}