zoukankan      html  css  js  c++  java
  • HNOI2010 平面图判定(planar)

    题目链接:戳我

    我怎么知道平面图有这个性质??

    对于一个平面图,它的边数不超过点数的(3n-6)

    所以可以直接把边数多的特判掉,剩下的图中边数和点数就是一个数量级的了。

    因为这个图存在欧拉回路,所以我们先把那些构成欧拉回路的边拉出来,将边上的两个端点的标号替换成在这个序列上的位置。然后判断这些边能不能不相交。

    对于两条边(i,j)(分别对应((u1,v1),(u2,v2))),如果(u1<u2<v1<v2)——

    那么这两个边肯定相交,不是平面图!!

    那么这两个边肯定一个在环的内部,一个在外部。这种只有两种状态进行规划,判断有没有合法方案的题——显然能想到2-SAT。

    我们连四条边,分别表示:

    • i在内部那么j一定在外部
    • i在外部那么j一定在内部
    • j在内部那么i一定在外部
    • j在外部那么i一定在内部

    然后tarjan判断一下有没有同一条边的两个状态在一个SCC里即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 100010
    using namespace std;
    int n,m,t,tot,cnt,kkk,top,T;
    int a[MAXN],b[MAXN],head[MAXN],pos[MAXN];
    int dfn[MAXN],low[MAXN],in[MAXN],st[MAXN],c[MAXN];
    struct Edge{int nxt,to,dis;}edge[MAXN<<1];
    struct Line{int u,v;}line[MAXN<<1];
    inline void add(int from,int to)
    {
        // printf("[%d %d]
    ",from,to);
        edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;
    }
    inline void tarjan(int x)
    {
        dfn[x]=low[x]=++tot;
        st[++top]=x;
        in[x]=1;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
            else if(in[v]) low[x]=min(low[x],dfn[v]);
        }
        if(dfn[x]==low[x])
        {
            int v;
            cnt++;
            do{v=st[top--],in[v]=0,c[v]=cnt;}while(x!=v);
        }
    }
    inline bool check()
    {
        for(int i=1;i<=m;i++)
            if(c[i]==c[i+m])
                return false;
        return true;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d",&T);
        while(T--)
        {
            memset(head,0,sizeof(head));
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            memset(in,0,sizeof(in));
            tot=top=cnt=t=kkk=0;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=m;i++) scanf("%d%d",&line[i].u,&line[i].v);
            for(int i=1;i<=n;i++) 
            {
                int x;
                scanf("%d",&x);
                pos[x]=i;
            }
            for(int i=1;i<=m;i++) line[i].u=pos[line[i].u],line[i].v=pos[line[i].v];
            if(m>3*n-6)
            {
                printf("NO
    ");
                continue;
            }
            for(int i=1;i<=m;i++)
                if(abs(line[i].u-line[i].v)!=1)
                {
                    line[++kkk]=line[i];
                    if(line[kkk].u>line[kkk].v) swap(line[kkk].u,line[kkk].v);
                }
            m=kkk;
            for(int i=1;i<=m;i++)
                for(int j=1;j<=m;j++)
                    if(line[i].u<line[j].u&&line[j].u<line[i].v&&line[j].v>line[i].v)
                        add(i+m,j),add(i,j+m),add(j,i+m),add(j+m,i);
            for(int i=1;i<=2*m;i++)
                if(!dfn[i])
                    tarjan(i);
            // for(int i=1;i<=m;i++)
                // printf("%d %d
    ",c[i],c[i+m]);
            if(check()==true) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    聊聊 print 的前世今生
    在树莓派里搭建 Lighttpd 服务器
    如何重复执行一条命令直至运行成功?
    手把手教你Windows Linux双系统的安装与卸载
    你以为只有马云会灌鸡汤?Linux 命令行也会!
    Linux 下三种提高工作效率的文件处理技巧
    太高效了!玩了这么久的Linux,居然不知道这7个终端快捷键!
    Linux下分析bin文件的10种方法
    Linux下几个与磁盘空间和文件尺寸相关的命令
    如何让你的脚本可以在任意地方都可执行?
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10884235.html
Copyright © 2011-2022 走看看