1. 程式人生 > >Luogu P3209 [HNOI2010]平面圖判定(2-SAT)

Luogu P3209 [HNOI2010]平面圖判定(2-SAT)

P3209 [HNOI2010]平面圖判定

題意

題目描述

若能將無向圖\(G=(V,E)\)畫在平面上使得任意兩條無重合頂點的邊不相交,則稱\(G\)是平面圖。判定一個圖是否為平面圖的問題是圖論中的一個重要問題。現在假設你要判定的是一類特殊的圖,圖中存在一個包含所有頂點的環,即存在哈密頓迴路。

輸入輸出格式

輸入格式:

輸入檔案的第一行是一個正整數\(T\),表示資料組數 (每組資料描述一個需要判定的圖)。接下來從輸入檔案第二行開始有\(T\)組資料,每組資料的第一行是用空格隔開的兩個正整數\(N\)\(M\),分別表示對應圖的頂點數和邊數。緊接著的\(M\)行,每行是用空格隔開的兩個正整數\(u\)

\(v\left(1\leq u,v\leq N\right)\),表示對應圖的一條邊\(\left(u,v\right)\), 輸入的資料保證所有邊僅出現一次。每組資料的最後一行是用空格隔開的\(N\)個正整數,從左到右表示對應圖中的一個哈密頓迴路\(V_1,V_2,\dots ,V_N\),即對任意\(i\not=j\)\(V_i\not=V_j\)且對任意\(1\leq i\leq N-1\)\(\left(V_i,V_i-1\right)\in E\)\(\left(V_1,V_N\right)\in E\)。輸入的資料保證\(100\%\)的資料滿足\(T\leq100,3\leq N\leq200,M\leq1000\)

輸出格式:

包含\(T\)行,若輸入檔案的第\(i\)組資料所對應圖是平面圖,則在第\(i\)行輸出 \(\text{YES}\),否則在第\(i\)行輸出\(\text{NO}\),注意均為大寫字母。

輸入輸出樣例

輸入樣例#1:

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

輸出樣例#1:

NO
YES

思路

這題毒瘤,要先轉對偶圖... --huyufeifei

其實這題也沒那麼毒瘤,直接\(2-SAT\)或者並查集就好了。

我們先把哈密頓迴路搬出來,畫成一個圓,再來考慮圖上其他的邊。其他的邊有兩種畫法:畫在圓內,畫在圓外。有些邊,如果都畫在圓內,就會相交,不符合平面圖的性質;而如果他們都畫在圓外,也會相交。所以我們可以把邊拿出來,把其是否畫在圓內作為這條邊的邊權,兩條邊不可相交作為其限制條件,做\(2-SAT\)

就好了。當然,\(alec\)神仙寫的是並查集做法,也是十分可行的。

還有一個剪枝:顯然平面圖中的邊的數量不能太多,因為圖越稠密越難畫出平面圖,所以噹噹前狀況下邊數過多時,我們就可以直接判NO。上網查資料得\(m\leq 3n-2\)(這個是可以用尤拉公式證明的。 --logeadd),加上剪枝就能跑得很快了。

AC程式碼

#include<bits/stdc++.h>
using namespace std;
const int MAXN=205;
const int MAXM=605;
int T,n,m,id[MAXN],inv[MAXN],u[10005],v[10005];
int nn,U[MAXM],V[MAXM];
int cnt,top[MAXM<<1],to[(MAXM*MAXM)<<2],nex[(MAXM*MAXM)<<2];
int tot,js,dfn[MAXM<<1],low[MAXM<<1],bel[MAXM<<1];
bool cir[MAXN][MAXN],vis[MAXM<<1];
stack<int>S;
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
void add_edge(int x,int y){to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;}
void tarjan(int now)
{
    dfn[now]=low[now]=++tot,vis[now]=true,S.push(now);
    for(int i=top[now];i;i=nex[i])
        if(!dfn[to[i]]) tarjan(to[i]),low[now]=min(low[now],low[to[i]]);
        else if(vis[to[i]]) low[now]=min(low[now],low[to[i]]);
    if(dfn[now]==low[now])
    {
        bel[now]=++js,vis[now]=false;
        while(S.top()!=now) bel[S.top()]=js,vis[S.top()]=false,S.pop();
        S.pop();
    }
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read(),nn=cnt=tot=js=0;
        memset(id,0,sizeof id);
        memset(inv,0,sizeof inv);
        memset(u,0,sizeof u);
        memset(v,0,sizeof v);
        memset(U,0,sizeof U);
        memset(V,0,sizeof V);
        memset(top,0,sizeof top);
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(bel,0,sizeof bel);
        memset(cir,false,sizeof cir);
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            if(x>y) swap(x,y);
            u[i]=x,v[i]=y;
        }
        for(int i=1;i<=n;i++)
        {
            int x=read();
            id[x]=i,inv[i]=x;
            if(i!=1)
            {
                int y=inv[i-1];
                if(x>y) swap(x,y);
                cir[x][y]=true;
            }
        }
        if(m>3*n-6)
        {
            puts("NO");
            continue;
        }
        if(inv[1]<inv[n]) cir[inv[1]][inv[n]]=true;
        else cir[inv[n]][inv[1]]=true;
        for(int i=1;i<=m;i++) if(!cir[u[i]][v[i]]) U[++nn]=u[i],V[nn]=v[i];
        for(int i=1;i<=nn;i++)
            for(int j=i+1;j<=nn;j++)
            {
                int x=id[U[i]],y=id[V[i]],xx=id[U[j]],yy=id[V[j]];
                if(x>y) swap(x,y);
                if(xx>yy) swap(xx,yy);
                if((x<xx&&xx<y&&y<yy)||(xx<x&&x<yy&&yy<y)) add_edge(i,j+nn),add_edge(i+nn,j),add_edge(j,i+nn),add_edge(j+nn,i);
            }
        for(int i=1;i<=(nn<<1);i++) if(!dfn[i]) tarjan(i);
        bool ans=true;
        for(int i=1;i<=nn;i++)
            if(bel[i]==bel[i+nn])
            {
                ans=false;
                break;
            }
        if(ans) puts("YES");
        else puts("NO");
    }
    return 0;
}