1. 程式人生 > 實用技巧 >題解 P3209 【[HNOI2010]平面圖判定】

題解 P3209 【[HNOI2010]平面圖判定】

首先需要了解平面圖的定義:

如果圖 \(G\) 能畫在平面 \(S\) 上,即 除頂點處外無邊相交 ,則稱 \(G\) 可平面嵌入 \(S\)\(G\) 為可平面圖或平面圖。

\(G\) 是平面圖,由 \(G\) 的邊將 \(G\) 所在的平面劃分成若干個區域,每個區域稱為 \(G\) 的一個 ,其中面積無限的面稱為無限面或外部面,面積有限的稱為有限面或內部面。包圍每個面的所有邊組成的迴路稱為該面的 邊界 ,邊界的長度稱為該面的 次數

為了強調平面圖的面,用三元組表示一個平面圖 \(G=(V,E,F)\) ,其中 \(F\) 表示平面圖 \(G\) 中面的個數。


對於連通平面圖,有尤拉公式:

\[V-E+F=2 \]

證明比較複雜,要涉及代數拓撲。

\(G\) 是連通的簡單平面圖(無重邊與自環),則每個面的次數都 至少為 \(3\) ,又因為每條邊僅屬於 兩個面 的邊界,則有: \(3F \leq 2E\) 。代入上式得:

\[E \leq 3V-6 \]


然後再來看這道題。

輸入中給出了這個圖 \(G\)\(G\) 中的一個哈密頓迴路。

我們將這個哈密頓迴路展開,即讓其他邊都在迴路圍成的區域內。

圖為樣例第一組資料:

不難發現,上圖中相交的兩條邊必須一條邊在哈密頓迴路內,一條在迴路外才能不相交。

則問題轉化為一個2-SAT問題,列舉相交的兩條邊 \(i,j\),連邊:

  • \(i \to j'\) 表示 \(i\) 在迴路內則 \(j\) 必須在迴路外。
  • \(j \to i'\) 表示 \(j\) 在迴路內則 \(i\) 必須在迴路外。
  • \(i' \to j\) 表示 \(i\) 在迴路外則 \(j\) 必須在迴路內。
  • \(j' \to i\) 表示 \(j\) 在迴路外則 \(i\) 必須在迴路內。

縮點後判斷是否存在 \(i,i'\) 在同一個強連通分量中,若存在則 \(G\) 不是平面圖。

然而 \(M \leq 10000\) ,直接連邊會爆炸。用上面的結論 \(K \leq 3V-6\) 可以特判掉不可能成為平面圖的情況。


\(\text{Code}:\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#define maxm 2000005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;

template <typename T>
inline T read()
{
	T x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

struct edge
{
	int u,v,next;
	inline bool operator < (const edge &T)const
	{
		return u<T.u||(u==T.u&&v<T.v);
	}
}e[maxm],eg[10005];

int head[maxm],k;

inline void add(int u,int v)
{
	e[k]=(edge){u,v,head[u]};
	head[u]=k++;
}

int n;
int dfn[maxm],low[maxm],scc[maxm],dfs_cnt,scc_cnt;
stack<int> S;

inline void tarjan(int u)
{
	S.push(u);
	dfn[u]=low[u]=++dfs_cnt;
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(!scc[v]) low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		int x;
		++scc_cnt;
		do
		{
			x=S.top();S.pop();
			scc[x]=scc_cnt;
		} while (x!=u);
	}
}

int N,M,G[205];

inline void clear()
{
	memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(scc,0,sizeof(scc));
	k=scc_cnt=dfs_cnt=0;
}

int main()
{
	// freopen("P3209.in","r",stdin);
	int T=read<int >();
	while(T--)
	{
		N=read<int >(),M=read<int >();
		clear();
		for(int i=1;i<=M;++i)
		{
			int u=read<int >(),v=read<int >();
			eg[i]=(edge){u,v};
		}
		for(int i=1;i<=N;++i)
			G[read<int >()]=i;
		if(M>3*N-6) {puts("NO");continue;}
		for(int i=1;i<=M;++i)
		{
			eg[i].u=G[eg[i].u];
			eg[i].v=G[eg[i].v];
			if(eg[i].u>eg[i].v) swap(eg[i].u,eg[i].v);
		}
		sort(eg+1,eg+M+1);
		for(int i=1;i<M;++i)
			for(int j=i+1;j<=M;++j)
				if(eg[i].u<eg[j].u&&eg[j].u<eg[i].v&&eg[i].v<eg[j].v)// 判斷是否相交
					add(i+M,j),add(j+M,i),add(i,j+M),add(j,i+M);
		for(int i=1;i<=(M<<1);++i)
			if(!dfn[i]) tarjan(i);
		bool flag=true;
		for(int i=1;i<=M;++i)
			if(scc[i]==scc[i+M])
			{
				flag=false;
				break;
			}
		puts(flag?"YES":"NO");
	}
	return 0;
}