【模板】2-SAT 问题

题面

https://www.luogu.org/problem/P4782

题解

输出时,要输出所属强连通分量的字典序最小的点,而且经实测,改了就错。

因为一个联通块,缩了点形成一个$DAG$,我们肯定要输出$DAG$上拓扑序靠后的$DAG$(附加条件更少),有因为$tarjan$是正着搜的,拓扑序靠后的强连通分量先被判定,字典序更小。

#include<cstdio>
#include<iostream>
#include<vector>
#include<stack>
#include<cstdlib>

using namespace std;

int n,m,clo=0,cnt=0;
int dfn[2000050],low[2000050],bel[2000050];
vector<int> to[2000050];
stack<int> stk;
bool ins[2000050],sat[1000050],vis[2000050];

void tarjan(int x){
  int i,y,l=to[x].size();
  dfn[x]=low[x]=++clo;
  ins[x]=true;
  stk.push(x);
  for (i=0;i<l;i++) {
    if (!dfn[to[x][i]]) {
      tarjan(to[x][i]);
      low[x]=min(low[x],low[to[x][i]]);
    }
    else if (ins[to[x][i]]) {
      low[x]=min(low[x],dfn[to[x][i]]);
    }
  }
  if (dfn[x]==low[x]) {
    ++cnt;
    int opt=(x-1)/n;
    int num=x-opt*n;
    do {
      y=stk.top();
      stk.pop();
      bel[y]=cnt;
      ins[y]=false;
    } while (y!=x);
  }
}

void dfs(int x){
  if (vis[x]) return;
  vis[x]=true;
  if (x>n) sat[x-n]=1; else sat[x]=0;
  int i,l=to[x].size();
  for (i=0;i<l;i++) if (!vis[to[x][i]])dfs(to[x][i]);
}

int main(){
  int i,u,v,opt1,opt2;
  scanf("%d %d",&n,&m);
  for (i=1;i<=m;i++) {
    scanf("%d %d %d %d",&u,&opt1,&v,&opt2);
    to[(!opt1)*n+u].push_back(opt2*n+v);
    to[(!opt2)*n+v].push_back(opt1*n+u);
  }
  for (i=1;i<=2*n;i++) if (!dfn[i]) tarjan(i);
  for (i=1;i<=n;i++) if (bel[i]==bel[n+i]) {
    puts("IMPOSSIBLE");
    return 0;
  }
  puts("POSSIBLE");
  for (i=1;i<=n;i++) if (bel[i]<bel[n+i]) dfs(i); else dfs(n+i);
  for (i=1;i<=n;i++) printf("%d ",sat[i]);
}
posted @ 2019-08-14 09:23  HellPix  阅读(146)  评论(0编辑  收藏  举报