【bzoj1997】[Hnoi2010]Planar

题目链接

Description

Input

Output

Sample Input

2

6 9

1 4

1 5

1 6

2 4

2 5

2 6

3 4

3 5

3 6

1 4 2 5 3 6

5 5

1 2

2 3

3 4

4 5

5 1

1 2 3 4 5

Sample Output

NO

YES

题解

把题目给的哈密顿回路拎出来。
对于不在哈密顿回路上的边,在平面图上要么把它放在环内部要么放在环外部。而对于相交的两条边,必然即不能同时放在内部也不能同时放在外部。
那么可以建立2-SAT模型:
去掉哈密顿回路上边后,设还剩m条边。
将这m条边分为m组,每组中两条边起点终点相同一条在外一条在里。这两条边中只能选择一条。而对于两条相交的边,一条取了外面的另一条就必须取里面的,反之同理。所以在2-SAT中从 A1i A2j 连边, A2i A1j 连边。缩点后若存在 A1i A2i 同属一个强联通分量,那么一定不是平面图,否则就是。

#include
using namespace std;

inline int read(){
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)) { if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int N = 10000 + 10, M = 1000000 + 10;
int n, m;
int u[N], v[N], pos[N];
int to[M], nxt[M], tot;
int hd[N], dfn[N], low[N], q[N], bl[N], dfs_clock, scc, top;
bool inq[N];

bool init(){
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
        u[i] = read(), v[i] = read();
    int x;
    for(int i = 1; i <= n; i++){
        x = read();
        pos[x] = i;
    }
    if(m > n * 3 - 6) return true;
    memset(hd, 0, sizeof(hd));
    return false;
}

inline void insert(int u, int v){to[++tot] = v; nxt[tot] = hd[u]; hd[u] = tot;}

void build(){
    int tmp = 0;
    for(int i = 1; i <= m; i++){
        u[i] = pos[u[i]]; v[i] = pos[v[i]];
        if(u[i] > v[i]) swap(u[i], v[i]);
        if(v[i] == u[i] + 1 || u[i] == 1 && v[i] == n) continue;
        u[++tmp] = u[i]; v[tmp] = v[i];
    }
    m = tmp;
    for(int i = 1; i <= m; i++)
    for(int j = i + 1; j <= m; j++)
        if((u[i] < u[j] && u[j] < v[i] && v[i] < v[j]) || (u[j] < u[i] && u[i] < v[j] && v[j] < v[i])){
            insert(2*i-1, 2*j);
            insert(2*i, 2*j-1);
            insert(2*j-1, 2*i);
            insert(2*j, 2*i-1);
        }
}

void tarjan(int u){
    dfn[u] = low[u] = ++dfs_clock;
    inq[u] = 1; q[++top] = u;
    for(int i = hd[u]; i; i = nxt[i]){
        int v = to[i];
        if(dfn[v] == -1){
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(inq[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u]){
        int now = -1;
        scc++;
        while(now != u){
            now = q[top--]; inq[now] = 0;
            bl[now] = scc;
        }
    }
}

void find_scc(){
    memset(dfn, -1, sizeof(dfn));
    memset(inq, 0, sizeof(inq));
    memset(low, 1, sizeof(low));
    for(int i = 1; i <= m<<1; i++)
        if(dfn[i] == -1) tarjan(i);
}

bool check(){
    for(int i = 1; i <= m; i++)
        if(bl[i*2] == bl[i*2-1]) return false;
    return true;
}

void work(){
    int t = read();
    while(t--){
        if(init()){
            puts("NO");
            continue;
        }
        build();
        find_scc();
        if(check()) puts("YES");
        else puts("NO");
    }
}

int main(){
    work();
    return 0;
}

你可能感兴趣的:(2-SAT,Tarjan)