[Hnoi2010]Planar

平面图判定

题目描述

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

输入输出格式

输入格式:

输入文件的第一行是一个正整数 $T$,表示数据组数 (每组数据描述一个需要判定的图)。接下来从输入文件第二行开始有 $T$ 组数据,每组数据的第一行是用空格隔开的两个正整数 $N$ 和 $M$,分别表示对应图的顶点数和边数。紧接着的 $M$ 行,每行是用空格隔开的两个正整数 $u$ 和 $v$ $left(1leq u,vleq N ight)$,表示对应图的一条边 $left(u,v ight)$, 输入的数据保证所有边仅出现一次。每组数据的最后一行是用空格隔开的 $N$ 个正整数,从左到右表示对应图中的一个哈密顿回路:$V_1,V_2,…,V_N$,即对任意 $i ot=j$ 有 $V_i ot=V_j$ 且对任意 $1leq ileq N-1$ 有 $left(V_i,V_i-1 ight)in E$ 及 $left(V_1,V_N ight)in E$。输入的数据保证 $100\%$ 的数据满足 $Tleq100,3leq Nleq200,Mleq10000$。

输出格式:

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

说明

感谢@hibiki 对题目进行修正

感谢@@Anguei 提供latex题面

题解

判断平面图有一个专门的算法,并且内容深奥,不适合信息竞赛使用。但这题有个欧拉回路,所以可以特殊化。参照xyz32768的博客。

首先,平面图的性质:边数小于等于(3n-6)。不符合的直接跳过。

然后可以发现,对于哈密尔顿环之外的任意一条边,要么连在环内部,要么连在环外部。在一定的条件下(可以简单判断),如果两条边同时连在环内部或同时连在环外部,这两条边就一定会相交,这样的限制条件符合2-SAT的模型。(原命题可以推出否命题,用并查集也行,但做这题就是为了练2-SAT的)

把第(i)条边拆成(i)(i')(i)表示这条边连在内部,(i')表示连在外部。

对于任意两条不在哈密尔顿环上的边(i,j,i eq j),如果他们不能同时连在环内或环外,则:

  1. 建边(<i,j'>),表示(i)在内则(j)必须在外。
  2. 建边(<i',j>),表示(i)在外则(j)必须在内。
  3. 建边(<j,i'>),表示(j)在内则(i)必须在外。
  4. 建边(<j',i>),表示(j)在外则(i)必须在内。

然后求一遍强连通分量,如果存在一个(i)(i')在同一个强连通分量里,那么原图不是平面图,否则是平面图。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=2e3+1,M=1e5+1;
int n,m,u[M],v[M],a[N],p[N];
int dfn[N],low[N],num;
int head[N],edge[M],next[M],tot;
int st[N],top,c[N],cnt;
bool ins[N];

il void add(int x,int y){
	edge[++tot]=y,next[tot]=head[x],head[x]=tot;
}
void tarjan(int x){
	dfn[x]=low[x]=++num;
	st[++top]=x,ins[x]=1;
	for(int i=head[x];i;i=next[i]){
		int y=edge[i];
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]){
		++cnt;
		int y;
		do{
			y=st[top--],ins[y]=0;
			c[y]=cnt;
		}while(y!=x);
	}
}
bool pd(){
	for(int i=1;i<=m;++i)
		if(c[2*i-1]==c[2*i]) return 0;
	return 1;
}
void Planar(){
	read(n),read(m);
	for(int i=1;i<=m;++i) read(u[i]),read(v[i]);
	tot=cnt=num=top=0;
	memset(head,0,sizeof head);
	memset(dfn,0,sizeof dfn); // edit 1: reset for tarjan
	memset(low,0,sizeof low);
	for(int i=1;i<=n;++i) read(a[i]);
	if(m>3*n-6) return puts("NO"),void();
	for(int i=1;i<=n;++i) p[a[i]]=i;
	for(int i=1;i<=m;++i){
		u[i]=p[u[i]],v[i]=p[v[i]];
		if(u[i]>v[i]) swap(u[i],v[i]);
		if(v[i]-u[i]==1||u[i]==1&&v[i]==n) continue;
		u[++top]=u[i],v[top]=v[i];
	}
	m=top;
	for(int i=1;i<=m;++i)for(int j=i+1;j<=m;++j)
		if(u[i]<u[j]&&u[j]<v[i]&&v[i]<v[j]||u[j]<u[i]&&u[i]<v[j]&&v[j]<v[i]){
			add(2*i-1,2*j),add(2*i,2*j-1);
			add(2*j-1,2*i),add(2*j,2*i-1);
		}
	top=0;
	for(int i=1;i<=m<<1;++i)
		if(!dfn[i]) tarjan(i);
	puts(pd()?"YES":"NO");
}
int main(){
	for(int t=read<int>();t--;) Planar();
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/11049070.html