感性理解支配樹
前言
orz一下這位大神。
本文獻給想要性感地理解支配樹的同學,如果你想更性感一點,所有證明均可跳過。
litble特別菜,有錯誤請指出,謝謝。
支配點
很久很久以前,有一張有向圖,有向圖有一個起點 ,有一個叫小X的強盜,佔據一個點攔路打劫。當小X佔據了 點後,若從 出發就到不了 點了,那麼 就是 的支配點。
而支配樹,就是滿足樹上一個點 的所有祖先都是它的支配點的樹。
How to build 支配樹
以下我們假定從 出發可以到達圖上所有點。
樹形圖
顯然,樹形圖自己就是自己的支配樹。
DAG
DAG的話,我們按照拓撲序從小到大進行,假設處理到點 ,則查一遍所有可達點 的點 ,所有點 一定被加入了支配樹中,那麼它們在支配樹上的LCA就是 在支配樹上的父親。
倍增就可以做到 ,例題洛谷P2597,程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=65540;
int n,top,js;
int f[N][16],du[N],p[N],st[N],ans[N],dep[N];
vector<int> g[N],rg[N],tr[N];
void topsort() {
for(RI i=1;i<=n;++i)
if(!du[i]) g[0].push_back(i),rg[i].push_back(0),++du[i];
top=1,st[top]=0;
while(top) {
int x=st[top];p[++js]=x,--top;
for(RI i=0;i<g[x].size();++i) {
--du[g[x][i]];
if(!du[g[x][i]]) st[++top]=g[x][i];
}
}
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(RI i=15;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(RI i=15;i>=0;--i) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
void dfs(int x) {
ans[x]=1;
for(RI i=0;i<tr[x].size();++i)
dfs(tr[x][i]),ans[x]+=ans[tr[x][i]];
}
int main()
{
n=read();
for(RI i=1;i<=n;++i) {
int x=read();
while(x) g[x].push_back(i),rg[i].push_back(x),++du[i],x=read();
}
topsort();
for(RI i=2;i<=n+1;++i) {
int x=p[i],y=rg[x][0];
for(RI j=1;j<rg[x].size();++j) y=lca(y,rg[x][j]);
tr[y].push_back(x),dep[x]=dep[y]+1,f[x][0]=y;
for(RI j=1;j<=15;++j) f[x][j]=f[f[x][j-1]][j-1];
}
dfs(0);
for(RI i=1;i<=n;++i) printf("%d\n",ans[i]-1);
return 0;
}
一般有向圖
一般有向圖有一個優秀的做法叫做Lengauer Tarjan,對,又是Tarjan,Tarjan tql。
首先,我們從 開始dfs整張圖,可以提取出一棵dfs樹,並且 的dfs序是 。
半支配點
假設存在一個點 ,從 出發有一條到 的路徑,並且路徑上任何一點 (不包括 和 )都滿足 ,則稱 為 的半支配點。
記 為 的dfn最小的半支配點,因為 在dfs樹上的父親也是它的一個半支配點,所以 一定是 的祖先。
我們為什麼需要這個 呢?因為我們刪掉原圖中的非樹邊後,連邊 ,不改變原圖中的支配點關係。性感的證明如下:
- 假如在原圖上刪掉 , 就不可達了,那麼顯然 是 在dfs樹上的祖先。
- 假若從 的某個祖先出發,可以在不經過 的情況下,走到一個 的點 , 就是 的支配點,反之不是。
- 因為不能經過 ,所以從這個祖先走到 的路徑上經過的所有點的 應該大於 。
- 假如這條路徑上的所有點的 都大於 ,則顯然通過 可以保證新圖上這個點依然能到 。否則,這條路徑要麼經過一個 小於等於 大於 的點(直接滿足條件),要麼全部經過 大於 的點(也就是 的半支配點)
- 所以,新圖中的支配點關係與原圖相同。
如果求出了 ,我們就把原圖變成了一個DAG,然後就可以重複DAG的做法啦。不過更優的做法也是有的。
求半支配點
對於一個點 ,我們找到所有邊 對應的 。
若