Luogu P3209 [HNOI2010]平面圖判定(2-SAT)
阿新 • • 發佈:2018-11-02
題意
題目描述
若能將無向圖\(G=(V,E)\)畫在平面上使得任意兩條無重合頂點的邊不相交,則稱\(G\)是平面圖。判定一個圖是否為平面圖的問題是圖論中的一個重要問題。現在假設你要判定的是一類特殊的圖,圖中存在一個包含所有頂點的環,即存在哈密頓迴路。
輸入輸出格式
輸入格式:
輸入檔案的第一行是一個正整數\(T\),表示資料組數 (每組資料描述一個需要判定的圖)。接下來從輸入檔案第二行開始有\(T\)組資料,每組資料的第一行是用空格隔開的兩個正整數\(N\)和\(M\),分別表示對應圖的頂點數和邊數。緊接著的\(M\)行,每行是用空格隔開的兩個正整數\(u\)
輸出格式:
包含\(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\)
還有一個剪枝:顯然平面圖中的邊的數量不能太多,因為圖越稠密越難畫出平面圖,所以噹噹前狀況下邊數過多時,我們就可以直接判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;
}