做到“[HNOI2010]平面圖判定”這道題的時候用到了平面圖的性質,於是來學學。
Reference1
Reference2
平面圖的定義
若簡單圖 \(G=(V,E)\) 能畫在平面上使得任意兩條無重合頂點的邊不相交,則稱 \(G\) 是平面圖(Planar Graph)。將 \(G\) 中由邊構成的且不能再划分成更小子區域的區域稱為平面圖 \(G\) 的面(Face),包圍這個區域的邊稱為面的邊界(Bound),其中由平面圖外部邊界組成的面稱為外部面或無限面(Exterior Face)。一個面 \(R\) 的邊界的邊數稱為面的度,記為 \(\deg(R)\)。除了割邊外,任何一條邊都屬於兩個面的邊界。
下面舉幾個例子。
Fig1. 一副非平面圖。可以發現無論如何調整點與邊的位置都無法讓邊不相交。
Fig2. 從 PPT 上盜的,關於平面圖及其各個面。
若干定理
定理 1 一個有限平面圖,其面的度之和等於其邊數的兩倍,即
根據前面的描述,定理 1 顯然成立。
定理 2(歐拉公式) 任何一個凸多面體(聯通平面圖)滿足關系式
其中 \(r\) 為圖的面數。
證明:對 \(e\) 施以歸納法。對於 \(e<2\) 的情況顯然成立。現在假設小於 \(e\) 的情況成立,考慮 \(e\) 條邊的圖 \(G\) 的情況。
- 若第 \(e\) 條邊一端為原來的點,一端為一個新的點,這時整幅圖增加了一個頂點與一條邊,面數不變,此時結論顯然仍成立。
- 若第 \(e\) 條邊兩端都為原來的點,則整幅圖增加了一個面與一條邊,頂點數不變,此時也滿足結論。
綜上,定理得證。
定理 2 的推論 設平面圖 \(G=(V,E)\) 有 \(k\) 個連通塊構成,則滿足關系式
根據之前所證,這個推論是顯然的。
定理 3 對於任意一個 \(v\ge 2\) 的聯通平面圖,滿足
證明:設 \(G\) 有 \(r\) 個面,由於每個面至少由三條邊組成,所以有
又由定理 1 知 \(2e\ge 3r\),代入歐拉公式消去 \(r\) 得 \(e\le 3v-6\),證畢。
這個結論相當重要,請務必牢記。
定理 3 的推論 任何聯通平面圖中,至少存在一個其度不超過 \(5\) 的節點。
證明沒看懂不過這個推論是很弱的,因為有很多非平面圖也滿足這個性質。
超簡單的小練習:證明 \(K_{3,3}\) 與 \(K_5\) 是非平面圖(套定理 3 就完事了)。
過家家級別的定理說完了,接下來請抓緊扶好,我們要跳級了。
Kuratowski 定理
Kuratowski 定理定性地說明了平面圖的本質。
在說這個定理之前,先引出一個定義:對於圖 \(G=(V,E)\) 的一條邊 \((u,v)\),先刪去這條邊,然后增加一個節點 \(w\),並連接 \((u,w),(v,w)\),這樣操作后所得的圖稱為原圖的細分圖。
Fig3. 從巨佬的 blog 上盜的,細分圖的例子。
Kuratowski 定理 一個圖是平面圖當且僅當它不包含與 \(K_{3,3}\) 或 \(K_5\) 的細分圖同構的子圖。
也就是說,如果 \(K_{3,3}\) 或 \(K_5\) 可以通過不斷細分變成這副圖,則這幅圖是非平面圖。(可以通過 Fig1 加深理解)
證明?沒找到……並且我還看見了這個……
行吧,所以虛晃一槍 qwq
[HNOI2010]平面圖判定 題解
好吧這其實才是我真正要說的……
這題還是很神的,一是有個沒見過的東西,二是對圖的處理屬實毒瘤。
首先注意到這題 \(m\le 10000\),但 \(n\le 200\),於是可以用上面的定理 2 把 \(m\) 的范圍縮小。
接下來如何判定這是個平面圖呢?似乎很棘手,但注意到這道題的圖有個特殊性質就是存在哈密頓圈。我們可以把這個哈密頓圈看成一個圓,除去在哈密頓圈上的邊后剩下的都是圓上的弦,那么如何判斷兩弦相交呢?考慮對於兩條弦 \((x,y),(u,v)\),其中 \(x,y,u,v\) 都是哈密頓圈上的順序編號,如果有 \(x<u<y<v\),那么這兩條弦就相交了。
於是對於每條弦,我們都要維護它的互斥關系,因為一條弦可以選擇畫在圓內或圓外來避免沖突,對於無法避免沖突的情況,就判定為非平面圖。
如何維護互斥關系?這隨便,二分圖、並查集、2-SAT 心情好寫哪個。(我用了並查集,好寫👌
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,fa[N],X[N],Y[N],id[N];
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
bool check()
{
for(int i=1;i<=m;++i) if(find(i)==find(i+m)) return 0;
return 1;
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) scanf("%d%d",&X[i],&Y[i]);
for(int i=1,x;i<=n;++i) scanf("%d",&x),id[x]=i;
if(m>3*n-6) {puts("NO"); continue;}
for(int i=1;i<=m<<1;++i) fa[i]=i;
for(int i=1,a,b;i<=m;++i)
{
a=id[X[i]],b=id[Y[i]];
X[i]=min(a,b),Y[i]=max(a,b);
}
for(int i=1;i<m;++i)
for(int j=i+1;j<=m;++j)
{
if(Y[i]==X[i]+1||(Y[i]==n&&X[i]==0)) continue;
if(Y[j]==X[j]+1||(Y[j]==n&&X[j]==0)) continue;
if(X[i]==X[j]||Y[i]==Y[j]||X[i]==Y[j]||X[j]==Y[i]) continue;
if((X[i]<X[j]&&Y[i]<Y[j]&&X[j]<Y[i])||
(X[j]<X[i]&&Y[j]<Y[i]&&X[i]<Y[j]))
fa[find(i)]=find(j+m),fa[find(j)]=find(i+m);
}
puts(check()?"YES":"NO");
}
return 0;
}