bzoj1997

题目链接: bzoj1997
题意:
给你一个包含所有点的哈密尔顿回路的图,判断是否是平面图。n<=200,m<=10000,T<=100
题解:
平面图的判定算法是一个很复杂的算法,然而这道题通过一个性质,便可以在原问题上加以转换。它告诉我们图中包含一个包含所有点的哈密尔顿回路。此时,我们不妨把哈密尔顿回路看成一个环,我们不难发现哈密尔顿回路以外的任意一条边,它都是连接了环上的两个点,我们不妨先把所有边看在圆内,此时可以发现,不满足这是一个平面图只当且仅当两条边一定相交,我们要使这两条边不相交,只当这两条边一个在圆内,一个在圆外。我们可以找出所有这样矛盾的边,然后把他们建边,此时我们需要判断的就是能否使得边被划分到两个域中,且每个域中无边。这就是经典的二分图染色了。我们成功地进行了转化。然而图中m<=10000,如果暴力建边一定会超时,我们有平面图定理:如果m>3*n-6,这一定不是平面图。
这样我们就能解决这个问题了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=10010;
int n,m,p,T;
int head[maxn],pos[maxn],color[maxn];
struct Edge{
    int to,next;
}edge[500010];
struct E{
    int a,b;
}e[maxn*3];
void build(int a,int b){
    edge[++p]=(Edge){b,head[a]};
    head[a]=p;
}
bool cross(int a,int b){
    int x1=pos[e[a].a],y1=pos[e[a].b],x2=pos[e[b].a],y2=pos[e[b].b];
    if (x1>y1) swap(x1,y1);
    if (x2>y2) swap(x2,y2);
    if ((x2>x1&&x2<y1&&y2>y1)||(x1>x2&&x1<y2&&y1>y2))
        return true;
    return false;
}
int line[maxn];
void solve(){
    int cl,op;
    for (int i=1;i<=m;i++)
        if (!color[i]){
            cl=0,op=1;
            line[op]=i;
            color[i]=2;
            for (;cl<op;){
                int now=line[++cl];
                for (int j=head[now];j;j=edge[j].next)
                    if (!color[edge[j].to]){
                        line[++op]=edge[j].to;
                        color[edge[j].to]=color[now]^1;
                    }
                    else if (color[edge[j].to]!=color[now]^1){
                        printf("NO\n");
                        return;
                    }

            }
        }
    printf("YES\n");
}
int main(){
    freopen("1997.in","r",stdin);
    freopen("1997.out","w",stdout);
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
            scanf("%d%d",&e[i].a,&e[i].b);
        for (int i=1;i<=n;i++){
            int a;
            scanf("%d",&a);
            pos[a]=i;
        }
        if (m>3*n-6){
            printf("NO\n");
            continue;
        }
        p=0;
        for (int i=1;i<=m;i++) color[i]=head[i]=0;
        for (int i=1;i<=m;i++)
            for (int j=i+1;j<=m;j++)
                if (cross(i,j)){
                    build(i,j);
                    build(j,i);
                }
        solve();
    }   
    fclose(stdin);
    fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值