#10097. 「一本通 3.5 练习 5」和平委员会(2-SAT 条件分析)

original link - https://loj.ac/problem/10097

题意:

给出2n个人,每两个( i _ i + 1 i\_i+1 i_i+1)为一对,只能且必须选其中一个。现在有m条限制 ( x , y ) (x,y) (x,y)表示 x , y x,y x,y不能同时选择,求出一个可行方案。

解析:

来分析一下条件:

i i i i + 1 i+1 i+1选一个: i → ¬ i + 1 i\to\neg i+1 i¬i+1 i + 1 → ¬ i i+1\to\neg i i+1¬i ¬ i → i + 1 \neg i\to i+1 ¬ii+1 ¬ i + 1 → i \neg i+1\to i ¬i+1i

( x , y ) (x,y) (x,y)不能同时选: x → ¬ y x\to \neg y x¬y y → ¬ x y\to \neg x y¬x

所以连上相应的边跑 t a r g a n targan targan就行了。

代码:

#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)

const int maxn=32009;
const int maxm=40009+2*maxn;

struct SCC {
     
    // edge
    int head[maxn],to[maxm],nex[maxm],now;
    void add(int a,int b) {
     
        nex[++now]=head[a];
        head[a]=now;
        to[now]=b;
    }
    // scc
    int low[maxn],dfn[maxn],scc[maxn],Idfs,Iscc;
    stack<int>sta;
    bool in[maxn];
    // init
    void init() {
     
        memset(head,0,sizeof head);
        memset(dfn,0,sizeof dfn);
        memset(in,0,sizeof in);
        now=0;
        Idfs=Iscc=0;
        while(!sta.empty())
            sta.pop();
    }
    // deal
    void tarjan(int p) {
     
        low[p]=dfn[p]=++Idfs;
        sta.push(p);
        in[p]=1;
        for(int i=head[p]; i; i=nex[i]) {
     
            int q=to[i];
            if(!dfn[q])
                tarjan(q),low[p]=min(low[p],low[q]);
            else if(in[q])
                low[p]=min(low[p],dfn[q]);
        }
        if(low[p]==dfn[p]) {
     
            ++Iscc;
            while(1) {
     
                int q=sta.top();
                sta.pop();
                scc[q]=Iscc;
                in[q]=0;
                if(p==q)
                    break;
            }
        }
    }
} E;

int main() {
     
    for(int n,m;scanf("%d%d",&n,&m)!=EOF;){
     
        E.init();
        rep(i,1,(n<<1)){
     
            E.add(i+2*n,i+1);
            E.add(i+1+2*n,i);
            E.add(i,i+1+2*n);
            E.add(i+1,i+2*n);
            i++;
        }
        rep(i,1,m){
     
            int a,b;scanf("%d%d",&a,&b);
            E.add(a,b+2*n);
            E.add(b,a+2*n);
        }
        rep(i,1,(n<<2)){
     
            if(!E.dfn[i])E.tarjan(i);
        }
        int cant=0;
        rep(i,1,(n<<1)){
     
            if(E.scc[i]==E.scc[i+2*n]){
     
                cant=1;break;
            }
        }
        if(cant){
     
            printf("NIE\n");
            continue;
        }
        rep(i,1,(n<<1)){
     
            if(E.scc[i]<E.scc[i+2*n]){
     
                printf("%d\n",i);
            }
        }
    }
}

你可能感兴趣的:(图论/搜索)