题目链接: bzoj1997
题意:
给你一个包含所有点的哈密尔顿回路的图,判断是否是平面图。n<=200,m<=10000,T<=100
题解:
平面图的判定算法是一个很复杂的算法,然而这道题通过一个性质,便可以在原问题上加以转换。它告诉我们图中包含一个包含所有点的哈密尔顿回路。此时,我们不妨把哈密尔顿回路看成一个环,我们不难发现哈密尔顿回路以外的任意一条边,它都是连接了环上的两个点,我们不妨先把所有边看在圆内,此时可以发现,不满足这是一个平面图只当且仅当两条边一定相交,我们要使这两条边不相交,只当这两条边一个在圆内,一个在圆外。我们可以找出所有这样矛盾的边,然后把他们建边,此时我们需要判断的就是能否使得边被划分到两个域中,且每个域中无边。这就是经典的二分图染色了。我们成功地进行了转化。然而图中m<=10000,如果暴力建边一定会超时,我们有平面图定理:如果m>3*n-6,这一定不是平面图。
这样我们就能解决这个问题了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=10010;
int n,m,p,T;
int head[maxn],pos[maxn],color[maxn];
struct Edge{
int to,next;
}edge[500010];
struct E{
int a,b;
}e[maxn*3];
void build(int a,int b){
edge[++p]=(Edge){b,head[a]};
head[a]=p;
}
bool cross(int a,int b){
int x1=pos[e[a].a],y1=pos[e[a].b],x2=pos[e[b].a],y2=pos[e[b].b];
if (x1>y1) swap(x1,y1);
if (x2>y2) swap(x2,y2);
if ((x2>x1&&x2<y1&&y2>y1)||(x1>x2&&x1<y2&&y1>y2))
return true;
return false;
}
int line[maxn];
void solve(){
int cl,op;
for (int i=1;i<=m;i++)
if (!color[i]){
cl=0,op=1;
line[op]=i;
color[i]=2;
for (;cl<op;){
int now=line[++cl];
for (int j=head[now];j;j=edge[j].next)
if (!color[edge[j].to]){
line[++op]=edge[j].to;
color[edge[j].to]=color[now]^1;
}
else if (color[edge[j].to]!=color[now]^1){
printf("NO\n");
return;
}
}
}
printf("YES\n");
}
int main(){
freopen("1997.in","r",stdin);
freopen("1997.out","w",stdout);
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&e[i].a,&e[i].b);
for (int i=1;i<=n;i++){
int a;
scanf("%d",&a);
pos[a]=i;
}
if (m>3*n-6){
printf("NO\n");
continue;
}
p=0;
for (int i=1;i<=m;i++) color[i]=head[i]=0;
for (int i=1;i<=m;i++)
for (int j=i+1;j<=m;j++)
if (cross(i,j)){
build(i,j);
build(j,i);
}
solve();
}
fclose(stdin);
fclose(stdout);
return 0;
}