P3209-平面图判定

平面图

平面图就是所有点的连边不相交的图。(当然是在你尽量想让它不相交的情况下)。这一点可以大概理解成拓扑图的性质,即每连一条边就会将某个区域进行分割——很明显,如果两个点分别处在两个不可达的区域,它们要连边显然是要穿过其他边的。

平面图定理

边数大于点数的三倍减六的图一定不是平面图。即设n为点数,m为边数,有

\[m<=n*3-6 \]

关于平面图的其他定理和上定理的证明我不会不过我有大佬博客就不乱搬了。

关于此题题解

首先,它给出了n和m,我们先通过平面图定理判断一下,这样可以偷很多的懒。

然后,我们发现这个题是给了哈密顿回路的。那么我们就可以把这个图伸展成一个环便于理解。
然后,我们发现了这个图的性质——哈密顿回路相当于分割整个平面成了两个区域。也就是说,两个非哈密顿回路上的边的边顶多能共存两组。(因为不能交叉)
两个区域?共存两组?
2-SAT浮现出水面。对,我们可以给非哈密顿回路边的边连边,然后求它们的最大匹配。
(如果没有哈密顿回路这玩意还是个DPC问题)
2-SAT的模板我就不注释了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
	int x=0,w=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return w?-x:x;
}

namespace star
{
	const int maxn=3e4+5,maxv=205,maxm=2e6+5;
	int cnt,Cir[maxv],rec[maxn],x1[maxn],y1[maxn],x2[maxn],y2[maxn];
	//Cir用来按编号从小到大记录环,rec反向记录Cir;
	//x1,y1,x2,y2分别记录全部边和去除环上边的剩余边 
	int tot,dfn[maxn],low[maxn],_n;
	//tarjan用 
	int ecnt,head[maxm],nxt[maxm],to[maxm],belong[maxn];
	//前向星用 
	bool cir[maxv][maxv];
	//cir记录Cir
	inline void addedge(int from,int too)
	{
		to[++ecnt]=too,nxt[ecnt]=head[from],head[from]=ecnt;
	}
	
	int st[maxn],top;
	bool vis[maxn];
	void tarjan(int x)
	{
		dfn[x]=low[x]=++tot;
		st[++top]=x;vis[x]=1;
		for(int i=head[x];i;i=nxt[i])
		{
			int u=to[i];
			if(!dfn[u]){
				tarjan(u);
				low[x]=min(low[x],low[u]);
			}else if(vis[x]){
				low[x]=min(low[x],dfn[u]);
			}
		}
		
		if (dfn[x] == low[x]) {
      	  int v; belong[x] = ++_n;vis[x]=0;
     	   while (v = st[top--], v != x) belong[v] = _n,vis[v]=0;
    	}
	}
	
	inline bool check()
	{
		for(int i=1;i<=(cnt<<1);i++)
			if(!dfn[i])tarjan(i);
		for(int i=1;i<=cnt;i++)
			if(belong[i]==belong[i+cnt])return false;
		return true;
	}
	
	inline void work()
	{
		int T,n,m;
		n=read(),m=read();
		
		memset(head,0,sizeof head);
		memset(x1,0,sizeof x1);
		memset(y1,0,sizeof y1);
		memset(x2,0,sizeof x2);
		memset(y2,0,sizeof y2);
		memset(belong,0,sizeof belong);
		memset(rec,0,sizeof rec);
		memset(cir,0,sizeof cir);
		memset(dfn,0,sizeof dfn);
		memset(low,0,sizeof low);
		memset(to,0,sizeof to);
		memset(nxt,0,sizeof nxt);
		memset(vis,0,sizeof vis);
		memset(st,0,sizeof st);
		ecnt=tot=cnt=_n=top=0;
		for(int i=1;i<=m;i++)
		{
			x1[i]=read(),y1[i]=read();
			if(x1[i]>y1[i]) swap(x1[i],y1[i]);
		}//记录下来等会处理 
		
		rec[Cir[1]=read()]=1;
		for(int i=2;i<=n;i++){
			rec[Cir[i]=read()]=i;
			(Cir[i]>Cir[i-1]?cir[Cir[i-1]][Cir[i]]:cir[Cir[i]][Cir[i-1]])=1;
		}
		(Cir[1]>Cir[n]?cir[Cir[n]][Cir[1]]:cir[Cir[1]][Cir[n]])=1;
		//获取cir和rec 
		
		if(m>3*n-6){
			printf("NO\n");return;
		}
		
		for(int i=1;i<=m;i++)
		{
			if(cir[x1[i]][y1[i]])continue;
			x2[++cnt]=x1[i],y2[cnt]=y1[i];
		}
		
		for(int i=1;i<cnt;i++)
			for(int j=i+1;j<=cnt;j++)
			{
				int a=rec[x2[i]] , b=rec[y2[i]] , x=rec[x2[j]] , y=rec[y2[j]];
				if(a>b)swap(a,b);if(x>y)swap(x,y);
				if((a<x and b>x and y>b) or (x<a and y>a and b>y))
					addedge(i,j+cnt),addedge(j,i+cnt),addedge(i+cnt,j),addedge(j+cnt,i);
			}
		if(check())printf("YES\n");
		else printf("NO\n");
	}
}

int main()
{
	int T=read();
	while(T--)	star::work();
	return 0;
}
posted @ 2020-07-28 14:41  Star_Cried  阅读(363)  评论(0编辑  收藏  举报