HNOI 2010 平面图判定 题解

题目传送门

题目大意: 给出一张包含一个哈密顿回路的图,问这个图是不是平面图(即可以画在同一个平面上且边不相交)。

题解

由于存在一个哈密顿回路(下面称作圆),所以每条边有三种连法:连在圆的内部,顺时针连在圆的外部,逆时针连在圆的外部,像这样:
HNOI 2010 平面图判定 题解_第1张图片
但事实上可以发现,连在外部的话顺时针连和逆时针连其实是等价的,这个手玩一下数据就能明白(其实就是不管顺时针还是逆时针,会相交的还是会相交,不会相交的怎么都不会相交),那么每条边就只有两种连法。

假如对圆上的点按顺序重新编号,那么每条边可以看作一个区间,假如两个区间相交但不包含,那么这两个区间对应的边是不能同时在圆的同一侧的(即一个在内,一个在外才能不相交)。

诶,这似乎可以用 2 − S A T 2-SAT 2SAT

边在内部还是外部对应 0 / 1 0/1 0/1 两种状态,分别设为点 i i i i ′ i' i,相交而不包含的边 a , b a,b a,b 之间 a a a b ′ b' b 连边, b b b a ′ a' a 连边,由于限制是相互的,所以要连无向边。然后看看有没有 i i i i ′ i' i 能相互到达就好了。

由于连的是无向边而且只关心连通性,所以可以用并查集来搞。

而且如果直接枚举所有的边来建图,那么时间复杂度为 O ( m 2 ) O(m^2) O(m2),实测会TLE两个点,所以还需要稍微优化一下,也就是把所有边按左端点排序,当枚举的第 j j j 条边的左端点已经大于当前的第 i i i 条边的右端点,那么 j j j 之后的边都不可能与 i i i 相交了,直接break。

代码如下:

#include 
#include 
#include 
using namespace std;
#define maxn 50010

int T,n,m;
struct section{
     int x,y;}sec[maxn],a[maxn];
int id[maxn],t;
bool cmp(section x,section y){
     return x.x<y.x;}
int f[maxn];
int findfa(int x){
     return x==f[x]?x:f[x]=findfa(f[x]);}
int link(int x,int y){
     x=findfa(x);y=findfa(y);if(x!=y)f[y]=x;}

int main()
{
     
	scanf("%d",&T);
	while(T--)
	{
     
		scanf("%d %d",&n,&m);
		for(int i=1,x,y;i<=m;i++)scanf("%d %d",&sec[i].x,&sec[i].y);
		for(int i=1,x;i<=n;i++)scanf("%d",&x),id[x]=i; t=0;
		for(int i=1;i<=m;i++)
		{
     
			int x=id[sec[i].x],y=id[sec[i].y];if(x>y)swap(x,y);
			if(x%n+1!=y&&y%n+1!=x)a[++t]=(section){
     x,y};
		}
		sort(a+1,a+t+1,cmp);
		for(int i=1;i<=2*t;i++)f[i]=i;
		for(int i=1;i<=t;i++)
		for(int j=i+1;j<=t;j++)
		{
     
			if(a[j].x>a[i].y)break;
			if(a[i].x<a[j].x&&a[j].x<a[i].y&&a[j].y>a[i].y)
			link(i,j+t),link(j,i+t);
		}
		bool ans=true;
		for(int i=1;i<=t;i++)
		if(findfa(i)==findfa(i+t)){
     ans=false;break;}
		if(ans)puts("YES"); else puts("NO");
	}
}

你可能感兴趣的:(题解_杂)