[HNOI2010]平面图判定

题意

一个图是平面图当且仅当所有的边两两不相交(端点除外),现在给定一个带环的图,判定它是不是平面图;多组数据

思路

考虑不在环上的边,连接两点的边无非只有两种连法,走环里面或外面,两条边如果可能会有相交部分,就只能一个走外面一个走里面,这显然可以是一个2-SAT问题

将边当作节点,一条边两个点,分别为走里面和走外面,连边跑tarjan即可

Code

#include<bits/stdc++.h>
#define N 3005
#define M 100005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int T,n,m;
int u[M],v[M],eu[M],ev[M],cir[205][205],ci[N],f[N];
int dfn[N],low[N],c,col,color[N];
int st[N],top;
struct Edge
{
	int next,to;
}edge[M<<2];int head[N],cnt;
void add_edge(int from,int to)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	head[from]=cnt;
}
template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
void init()
{
	memset(cir,0,sizeof(cir));
	memset(dfn,0,sizeof(dfn));
	memset(color,0,sizeof(color));
	memset(head,0,sizeof(head));
	cnt=c=col=top=0;
}
void tarjan(int rt)
{
	dfn[rt]=low[rt]=++c;
	st[++top]=rt;
	for(int i=head[rt];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[rt]=Min(low[rt],low[v]);
		}
		else if(!color[v]) low[rt]=Min(low[rt],dfn[v]);
	}
	if(dfn[rt]==low[rt])
	{
		color[rt]=++col;
		while(st[top]!=rt) color[st[top--]]=col;
		--top;
	}
}
void lk()
{
	int tot=0;
	for(int i=1;i<=m;++i) if(!cir[u[i]][v[i]]) eu[++tot]=u[i],ev[tot]=v[i];
	m=tot;
	for(int i=1;i<m;++i)
	{
		int u1=f[eu[i]],v1=f[ev[i]];
		if(u1>v1) swap(u1,v1);
		for(int j=i+1;j<=m;++j)
		{
			int u2=f[eu[j]],v2=f[ev[j]];
			if(u2>v2) swap(u2,v2);
			if((u1 < u2 && u2 < v1 && v1 < v2) || (u2 < u1 && u1 < v2 && v2 < v1))
			{
				add_edge(i,j+m);
				add_edge(i+m,j);
				add_edge(j,i+m);
				add_edge(j+m,i);
			}
		}
	}
}
int main()
{
	read(T);
	while(T--)
	{
		read(n);read(m);
		init();
		for(int i=1;i<=m;++i) read(u[i]),read(v[i]);
		for(int i=1;i<=n;++i) read(ci[i]),f[ci[i]]=i;
		for(int i=1;i<n;++i) cir[ci[i]][ci[i+1]]=cir[ci[i+1]][ci[i]]=1;
		cir[ci[n]][ci[1]]=cir[ci[1]][ci[n]]=1;
		if(m>3*n-6) { puts("NO"); continue; }
		
		lk();
		for(int i=1;i<=m*2;++i) if(!dfn[i]) tarjan(i);
		bool no=0;
		for(int i=1;i<=m;++i) if(color[i]==color[i+m]) no=1;
		printf(no ? "NO\n" : "YES\n");
	}
	return 0;
}
posted @ 2019-10-12 09:58  擅长平地摔的艾拉酱  阅读(154)  评论(0编辑  收藏  举报
/*取消选中*/