[HNOI2010] 平面图判定

题目大意

判定一个具有哈密顿回路的图是否是平面图。

题解

我们把这个哈密顿回路的点按顺序拎出来分布在环上,继续观察这题的性质。(图在 csacademy 实在太难画了,摆)接下来我们只关心不是环边的环。

如果一条边与另一条边不在结点上相交,那么必定有一条要放到环外面,另一条放到环里面。拓展一下,如果三条边两两不在结点相交,那么一定不是平面图,总有一条边会与另外的边相交。
听上去是不是很像二分图?但是观察到 m m m 的范围实在是太大了,建图恐怕是不行的,毕竟还要有枚举的时间,于是我们直接观察平面图的性质。

OI-wiki 中关于平面图的讲解

在考场上我们没法严谨证明这些结论,但是平面图显然边数会有限制,我们可以去构造其偶环数量最多的情况,从而限制得到 m ⩽ 3 n − 6 m\leqslant 3n-6 m3n6
得到了此结论后就可以随便做了,比如说二分图染色,2-SAT,并查集等等,这里用的是二分图染色,时间复杂度 O ( T n 2 ) \mathcal{O}(Tn^2) O(Tn2)
如何判断两条边是否相交?考虑将环上的点先铺成链,然后你会发现如果 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 相交且不包含,则 l 1 < l 2 < r 1 < r 2 l_1<l_2<r_1<r_2 l1<l2<r1<r2(这里我们必须保证不在结点处相交),跟线段的判别是一致的。
十分需要注意的是哈密顿回路不一定按照 1 ∼ n 1\sim n 1n 的顺序给你,如果你想更方便处理的话,你可以离散化一下。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
const int inf=1e18;
struct edge{int x,y,vis;}a[N],b[N];
struct node{int to,nxt;}e[N];
int f[N],cnt,head[N],tot,col[N];
inline int read(){
    char op=getchar();
    int w=0,s=1;
    while(op<'0'||op>'9'){
        if(op=='-') s=-1;
        op=getchar();
    }
    while(op>='0'&&op<='9'){
        w=(w<<1)+(w<<3)+op-'0';
        op=getchar();
    }
    return w*s;
}
bool cmp(edge x,edge y){
    if(x.x==y.x) return x.y<y.y;
    return x.x<y.x;
}
bool check(edge x,edge y){
    if(x.x==y.x||x.y==y.y||x.y==y.x||x.x==y.y) return false;
    if(x.x<y.x&&y.x<x.y&&x.y<y.y) return true;
    return false;
}
void add(int x,int y){
    e[++tot].to=y;
    e[tot].nxt=head[x];
    head[x]=tot;
}
bool dfs(int x,int k){
    col[x]=k;
    for(register int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(!col[y]){
            col[y]=3-k;
            int p=dfs(y,3-k);
            if(!p) return false;
        }else if(col[y]==k){
            return false;
        }else continue;
    }
    return true;
}
signed main(){
    int T=read();
    while(T--){
        int n=read(),m=read();
        cnt=0;
        for(register int i=1;i<=m;i++){
            a[i].x=read();
            a[i].y=read();
            a[i].vis=false;
        }
        for(register int i=1;i<=n;i++) f[i]=read();
        if(m>3*n+6){
            printf("NO\n");
            continue;
        }
        memset(head,0,sizeof(head));
        f[n+1]=f[1];
        map<int,int> mp;
        cnt=0;
        for(register int i=1;i<=n;i++) mp[f[i]]=++cnt;//离散化一下
        cnt=0;
        for(register int i=1;i<=m;i++){
            b[++cnt].x=mp[a[i].x];
            b[cnt].y=mp[a[i].y];
            if(b[cnt].x>b[cnt].y) swap(b[cnt].x,b[cnt].y);
        }
        sort(b+1,b+m+1,cmp);
        bool c=false;
        tot=0;
        memset(col,0,sizeof(col));
        for(register int i=1;i<=m;i++){
            for(register int j=i+1;j<=m;j++){
                if(check(b[i],b[j])){
                    add(i,j),add(j,i);
                }
            }
        }
        for(register int i=1;i<=m;i++){
            if(!col[i]){
                int k=dfs(i,1);
                if(!k) c=true;
            }
        }
        if(c) printf("NO\n");
        else printf("YES\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值