[HNOI2010]PLANAR

题目描述

若能将无向图G=(V,E)画在平面上使得任意两条无重合顶点的边不相交,则称G是平面图。判定一个图是否为平面图的问题是图论中的一个重要问题。现在假设你要判定的是一类特殊的图,图中存在一个包含所有顶点的环,即存在哈密顿回路。

输入输出格式

输入格式:

输入文件的第一行是一个正整数T,表示数据组数(每组数据描述一个需要判定的图)。接下来从输入文件第二行开始有T组数据,每组数据的第一行是用空格隔开的两个正整数N和M,分别表示对应图的顶点数和边数。紧接着的M行,每行是用空格隔开的两个正整数u和v(1<=u,v<=n),表示对应图的一条边(u,v),输入的数据保证所有边仅出现一次。每组数据的最后一行是用空格隔开的N个正整数,从左到右表示对应图中的一个哈密顿回路:V1,V2,…,VN,即对任意i≠j有Vi≠Vj且对任意1<=i<=n-1有(Vi,Vi-1) ∈E及(V1,Vn) ∈E。输入的数据保证100%的数据满足T<=100,3<=N<=200,M<=10000。

输出格式:

包含T行,若输入文件的第i组数据所对应图是平面图,则在第i行输出YES,否则在第i行输出NO,注意均为大写字母

思路

先用平面图定理 m<=3*n+6 把边数减小到O(n)级别。

然后设 集合 i+m 表示 不能与i共存的边 所在的集合。

枚举每一对不能共存的边,如果已经在同一个集合内,则无解。

否则交叉连边 fa[find(i)]=j+m , fa[find(j)]=i+m 。

如何判断两条边是否相交?先把所有边坐标转换为哈密顿回路中的顺序坐标,对于任意两条边i,j,如果有xi

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2e3+1e1;

int x[maxn<<4],y[maxn<<4],vis[maxn<<4];
int id[maxn];
int fa[maxn<<5];
int T,n,m;

inline int findfa(int x)
{
    return fa[x] == x ? x : fa[x] = findfa(fa[x]);
}

inline void initfa()
{
    for(int i=1;i<=m<<1;i++)
        fa[i] = i;
}

inline bool cross(int x1,int x2,int y1,int y2)
{
    if( x1 == x2 || y1 == y2 || x1 == y2 || x2 == y1 )
        return 0;
    if( x1 < x2 && y1 < y2 && x2 < y1 )
        return 1;
    if( x2 < x1 && y2 < y1 && x1 < y2 )
        return 1;
    return 0;
}

inline bool check()
{
    initfa();
    for(int i=1;i<=m;i++)
    {
        if( vis[i] )
            continue;
        for(int j=1;j<=m;j++)
        {
            if( vis[j] )
                continue;
            if( !cross(x[i],x[j],y[i],y[j]) )
                continue;
            int fai = findfa(i) , faj = findfa(j);
            if( fai == faj )
                return 0;
            fa[fai] = findfa( j + m ),
            fa[faj] = findfa( i + m );
        }
    }
    return 1;
}

inline void init()
{
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    memset(vis,0,sizeof(vis));
    n = m = 0;
}

inline int getint()
{
    int ret = 0;
    char ch = getchar();
    while( ch < '0' || ch > '9' )
        ch = getchar();
    while( '0' <= ch && ch <= '9' )
        ret = ret * 10 + ( ch - '0' ),
        ch = getchar();
    return ret;
}
int main()
{
    T = getint();

    while( T-- )
    {
        init();
        n = getint() , m = getint();
        for(int i=1;i<=m;i++)
            x[i] = getint() , y[i] = getint();
        for(int i=1;i<=n;i++)
            id[getint()] = i;
        if( m > 3 * n + 6 )
        {
            puts("NO");
            continue;
        }
        for(int i=1,a,b;i<=m;i++)
        {
            a = id[x[i]] , b = id[y[i]];
            x[i] = min( a , b ),
            y[i] = max( a , b );
        }
        for(int i=1;i<=m;i++)
            if( y[i] == x[i] + 1 || ( y[i]==n && x[i]==0) )
                vis[i] = 1;
        if( check() )
            puts("YES");
        else puts("NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值