[Luogu P3209] [HNOI2010] 平面图判定

洛谷传送门

题目描述

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

输入输出格式

输入格式:

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

输出格式:

包含 T T 行,若输入文件的第i组数据所对应图是平面图,则在第 i i 行输出YES,否则在第 i i 行输出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

解题分析

如果没有哈密顿回路这个条件这道题显然十分不可做(据Rockdu Dark♂ lao说是判子图)。

不过已经有了一个环, 我们只需要考虑非环边。 显然每条非环边可以接在外面或里面, 所以每两条可能相交的边的编号我们连4条边, 然后一发 tarjan t a r j a n 即可AC…

不过这道题有个剪枝:一个平面图的总边数 m m 一定是n×36的。

证明:根据欧拉公式 nm+r=2 n − m + r = 2 n n 个顶点, m条边, r r 个面),和3×r=2×m(每条边被两个面共享,每个面由三条边构成)可得。

所以那些边有几千条的根本不用跑…

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ME 40050
#define MX 510
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int T, dot, line, col, cnt, ecnt, top, arr;
int head[ME], dfn[ME], low[ME], sta[ME];
int loop[ME], pos[MX], bel[ME];
bool mp[MX][MX], vis[ME];
struct Edge
{
    int to, nex;
}edge[ME << 1];
struct EDGE
{
    int from, to;
}e[ME << 1], oth[ME << 1], d1, d2;
IN void reset()
{
    std::memset(mp, 0, sizeof(mp));
    std::memset(low, 0, sizeof(low));
    std::memset(bel, 0, sizeof(bel));
    std::memset(dfn, 0, sizeof(dfn));
    for (R int i = 1; i <= ecnt; ++i)
    head[i] = head[i + line] = 0;
    cnt = ecnt = col = top = arr = 0;
}
IN void addedge(const int &from, const int &to)
{edge[++cnt] = {to, head[from]}; head[from] = cnt;}
IN bool check(const int &a, const int &b)
{
    d1 = oth[a], d2 = oth[b];
    if(d1.from > d1.to) std::swap(d1.from, d1.to);
    if(d2.from > d2.to) std::swap(d2.from, d2.to);
    if(d1.from > d2.from) std::swap(d1, d2);
    if(d1.from == d2.from || d1.to == d2.to) return false;
    if(d1.to > d2.from && d1.to < d2.to) return true; return false;
}
void tarjan(const int &now)
{
    dfn[now] = low[now] = ++arr;
    sta[++top] = now, vis[now] = true;
    for (R int i = head[now]; i; i = edge[i].nex)
    {
        if(!dfn[edge[i].to]) tarjan(edge[i].to), low[now] = std::min(low[now], low[edge[i].to]);
        else if(vis[edge[i].to]) low[now] = std::min(low[now], dfn[edge[i].to]);
    }
    if(dfn[now] == low[now])
    {
        R int tp = 0; ++col;
        W (tp != now)
        {
            tp = sta[top--];
            bel[tp] = col, vis[tp] = false;
        }
    }
}
int main(void)
{
    in(T); int bd;
    W (T--)
    {
        reset(); in(dot), in(line);
        for (R int i = 1; i <= line; ++i) in(e[i].from), in(e[i].to);
        for (R int i = 1; i <= dot; ++i)
        {
            in(loop[i]); pos[loop[i]] = i;
            mp[loop[i]][loop[i - 1]] = true;
            mp[loop[i - 1]][loop[i]] = true;
        }
        if(dot * 3 - 6 < line) goto err;
        mp[loop[1]][loop[dot]] = true, mp[loop[dot]][loop[1]] = true;
        for (R int i = 1; i <= line; ++i)
        {
            if(mp[e[i].from][e[i].to]) continue;
            oth[++ecnt] = {pos[e[i].from], pos[e[i].to]};
        }
        for (R int i = 1; i < ecnt; ++i)
        {
            for (R int j = i + 1; j <= ecnt; ++j)
            {
                if(check(i, j))
                {
                    addedge(i, j + line), addedge(i + line, j);
                    addedge(j + line, i), addedge(j, i + line);
                }
            }
        }
        bd = ecnt + line;
        for (R int i = 1; i <= ecnt; ++i) if(!dfn[i]) tarjan(i);
        for (R int i = 1 + line; i <= bd; ++i) if(!dfn[i]) tarjan(i);
        for (R int i = 1; i <= ecnt; ++i)
        if(bel[i] == bel[i + line]) goto err;
        printf("YES\n"); continue; err: printf("NO\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值