[Hnoi2010]Planar
平面图判定
题目描述
若能将无向图 $G=(V, E)$ 画在平面上使得任意两条无重合顶点的边不相交,则称 $G$ 是平面图。判定一个图是否为平面图的问题是图论中的一个重要问题。现在假设你要判定的是一类特殊的图,图中存在一个包含所有顶点的环,即存在哈密顿回路。
输入输出格式
输入格式:输入文件的第一行是一个正整数 $T$,表示数据组数 (每组数据描述一个需要判定的图)。接下来从输入文件第二行开始有 $T$ 组数据,每组数据的第一行是用空格隔开的两个正整数 $N$ 和 $M$,分别表示对应图的顶点数和边数。紧接着的 $M$ 行,每行是用空格隔开的两个正整数 $u$ 和 $v$ $\left(1\leq u,v\leq N\right)$,表示对应图的一条边 $\left(u,v\right)$, 输入的数据保证所有边仅出现一次。每组数据的最后一行是用空格隔开的 $N$ 个正整数,从左到右表示对应图中的一个哈密顿回路:$V_1,V_2,…,V_N$,即对任意 $i\not=j$ 有 $V_i\not=V_j$ 且对任意 $1\leq i\leq N-1$ 有 $\left(V_i,V_i-1\right)\in E$ 及 $\left(V_1,V_N\right)\in E$。输入的数据保证 $100\%$ 的数据满足 $T\leq100,3\leq N\leq200,M\leq10000$。
输出格式:包含 $T$ 行,若输入文件的第 $i$ 组数据所对应图是平面图,则在第 $i$ 行输出 $\text{YES}$,否则在第 $i$ 行输出 $\text{NO}$,注意均为大写字母
输入输出样例
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
NO YES
说明
感谢@hibiki 对题目进行修正
感谢@@Anguei 提供latex题面
题解
判断平面图有一个专门的算法,并且内容深奥,不适合信息竞赛使用。但这题有个欧拉回路,所以可以特殊化。参照xyz32768的博客。
首先,平面图的性质:边数小于等于\(3n-6\)。不符合的直接跳过。
然后可以发现,对于哈密尔顿环之外的任意一条边,要么连在环内部,要么连在环外部。在一定的条件下(可以简单判断),如果两条边同时连在环内部或同时连在环外部,这两条边就一定会相交,这样的限制条件符合2-SAT的模型。(原命题可以推出否命题,用并查集也行,但做这题就是为了练2-SAT的)
把第\(i\)条边拆成\(i\)和\(i'\),\(i\)表示这条边连在内部,\(i'\)表示连在外部。
对于任意两条不在哈密尔顿环上的边\(i,j,i\neq j\),如果他们不能同时连在环内或环外,则:
- 建边\(<i,j'>\),表示\(i\)在内则\(j\)必须在外。
- 建边\(<i',j>\),表示\(i\)在外则\(j\)必须在内。
- 建边\(<j,i'>\),表示\(j\)在内则\(i\)必须在外。
- 建边\(<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;
}