简述
有 n
个变量,每一个变量有且仅有 0/1
两种取值。通过高效建边,可以用 tarjan缩点
算法在 O(m)
时间内解决。
实现
关键:建边。
定理1:![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/38adadfe404a617ffa885840519727af.png)
证明:该定理是对缩点后的 DAG
按拓扑序自底向上进行选择、删除得到的结论。换句话说,如果当前强联通里的点有的已经选择了,那么整个强联通都不能选择。
定理2:
证明:因为存在等价逆否命题。
应用
例1. 2-SAT 问题
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mx=4e6+5;
int n,m,num,cnt,val[mx],c[mx],dfn[mx],vis[mx],low[mx];
char op[10];
stack<int> s;
vector<int> g[mx];
//考点:2-sat 建边
void tarjan(int x) {
dfn[x]=low[x]=++num,vis[x]=1,s.push(x);
for(auto z:g[x]) {
if(!dfn[z]) {
tarjan(z),low[x]=min(low[x],low[z]);
}
else if(vis[z]) low[x]=min(low[x],dfn[z]);
}
if(dfn[x]==low[x]) {
cnt++;
int tmp=0;
do{
tmp=s.top();
s.pop();
c[tmp]=cnt,vis[tmp]=0;
}while(tmp!=x);
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int x,y,z; scanf("%d%d%d%s",&x,&y,&z,op);
if(op[0]=='A') {
if(z) {
g[x].push_back(x+n);
g[y].push_back(y+n);
}
else {
g[x+n].push_back(y);
g[y+n].push_back(x);
}
}
else if(op[0]=='O') {
if(z) {
g[x].push_back(y+n);
g[y].push_back(x+n);
}
else {
g[x+n].push_back(x);
g[y+n].push_back(y);
}
}
else {
if(z) {
g[x].push_back(y+n);
g[x+n].push_back(y);
g[y].push_back(x+n);
g[y+n].push_back(x);
}
else {
g[x].push_back(y);
g[x+n].push_back(y+n);
g[y].push_back(x);
g[y+n].push_back(x+n);
}
}
}
for(int i=1;i<=2*n;i++) {
if(!low[i]) {
tarjan(i);
}
}
for(int i=1;i<=n;i++) {
if(c[i]==c[i+n]) {
printf("IMPOSSIBLE");
return 0;
}
}
printf("POSSIBLE\n");
for(int i=1;i<=n;i++) {
val[i]=c[i]<c[i+n]; //为真
printf("%d ",!val[i]);
}
}
例2. ABC210F. Coprime Solitaire
提示:本题首先要抽象出 2-sat
的模型,然后构造新的状态(前缀和),减少子句数量。