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;
}
posted @ 2018-11-02 07:55  UranusITS  阅读(196)  评论(0编辑  收藏  举报