支配树详解
什么是支配樹?
對于一張有向圖,確定一個(gè)根,如果根到 x 的每條路徑都經(jīng)過 y,那么稱 y 是 x 支配點(diǎn)。求出原圖的一個(gè) dfs 樹,那么 x 的支配點(diǎn)一定在 x 到根的鏈上。如果每個(gè)點(diǎn)向自己深度最深的支配點(diǎn)連邊,就構(gòu)成了支配樹。
明確一些有向圖 dfs 樹的性質(zhì):
1.樹邊總是由 dfn 小的點(diǎn)指向 dfn 大的點(diǎn),非樹邊(兩端點(diǎn)無祖先關(guān)系)總是由 dfn 大的點(diǎn)指向 dfn 小的點(diǎn)。
2.若 v,w 是圖中節(jié)點(diǎn)且 dfn[v]<=dfn[w],則任意從 v 到 w 的路徑必然包含它們在 dfs 樹中的一個(gè)公共祖先。
如果存在 y 到 x 的一條路徑,使得路徑上的點(diǎn)(除 x,y)dfn 都大于 x,那么稱 y 是 x 的一個(gè)半支配點(diǎn)。可以證明 x 的半支配點(diǎn)一定是 x 的祖先,我們記 sdom[x] 為 x 半支配點(diǎn)中深度最小的點(diǎn)。
求 sdom:考慮枚舉路徑上最后一個(gè)點(diǎn) z,sdom[x]=min?{sdom[z]∣(z,x)∈E,dfn[z]>dfn[x]}sdom[x]=\min\{sdom[z]|(z,x)\in E,dfn[z]>dfn[x]\}sdom[x]=min{sdom[z]∣(z,x)∈E,dfn[z]>dfn[x]}。
怎么求支配點(diǎn)?對于一個(gè)點(diǎn) x,sdom[x] 到 x 之間的點(diǎn)一定不會(huì)是支配點(diǎn)。對于 x 到根的每個(gè)點(diǎn) y,把不合法的點(diǎn)刪掉后剩下的深度最深的點(diǎn)就是支配點(diǎn)。
實(shí)現(xiàn):
1.求 sdom:相當(dāng)于求 x 到根的路徑上 dfn 大于 dfn[x] 的點(diǎn)的最小值。按 dfn 從大到小處理,帶權(quán)并查集維護(hù)子樹。
2.求 idom:相當(dāng)于求 x 到根的路徑上深度大于 sdom[x] 的點(diǎn)的最小值。同樣按 sdom[x] 從大到小維護(hù)帶權(quán)并查集。注意桶排。
(可能)比較好寫的做法:求完 sdom 以后 sdom[x] 向 x 連邊。把圖變?yōu)?DAG.
洛谷模板:
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=200010,M=300010; struct edge{int to,next; }ed[M]; int sz,head[N],dfn[N],deep[N],fa[N],size[N],a[N],c[N],tim,sdom[N],idom[N],wb[N],lk[N],tr[M]; vector <int> b[N],d[N]; void add_edge(int from,int to) {ed[++sz].to=to;ed[sz].next=head[from];head[from]=sz; } int read() {int x=0;char c=getchar(),flag='+';while(!isdigit(c)) flag=c,c=getchar();while(isdigit(c)) x=x*10+c-'0',c=getchar();return flag=='-'?-x:x; } inline int chk(int x,int y) {if(!x||!y) return x|y;if(dfn[x]<dfn[y]) return x;return y; } inline int chk2(int x,int y) {if(dfn[sdom[x]]<dfn[sdom[y]]) return x;return y; } void dfs(int u) {dfn[u]=++tim;a[tim]=u;d[deep[u]].push_back(u);for(int i=head[u];i;i=ed[i].next){int v=ed[i].to;if(dfn[v]) continue;tr[i]=1;deep[v]=deep[u]+1;dfs(v);} } int find(int x) {if(x==fa[x]) return x;int y=fa[x];fa[x]=find(y);if(y^fa[x]) c[x]=chk2(c[x],c[y]); //帶權(quán)并查集 c[x] 維護(hù) x 到 fa 的最小值(不包括 fa)return fa[x]; } int main() {int n=read(),m=read();for(int i=1;i<=m;i++){int u=read(),v=read();add_edge(u,v);b[v].push_back(u);}deep[1]=1;dfs(1);//get sdomfor(int i=1;i<=n;i++) fa[i]=i;for(int i=n;i>=1;i--){int x=a[i];for(int j=0;j<b[x].size();j++){int y=b[x][j];sdom[x]=chk(sdom[x],y);find(y);sdom[x]=chk(sdom[x],chk(sdom[c[y]],sdom[c[fa[y]]]));}c[x]=x;for(int j=head[x];j;j=ed[j].next){int y=ed[j].to;if(tr[j]) fa[y]=x; //特判樹邊}}//get idomfor(int i=1;i<=n;i++) fa[i]=i;for(int i=1;i<=n;i++) wb[deep[sdom[i]]]++;for(int i=n;i>=1;i--) wb[i]+=wb[i+1];for(int i=1;i<=n;i++) a[wb[deep[sdom[i]]]--]=i;int p=n+1;for(int i=1;i<=n;i++){int x=a[i];while(p>deep[sdom[x]]) {p--;for(int j=0;j<d[p].size();j++) {int y=d[p][j];c[y]=y;for(int k=head[y];k;k=ed[k].next){int z=ed[k].to;if(tr[k]) fa[z]=y; //特判樹邊}}}int y=find(x);if(sdom[c[x]]==sdom[x]) idom[x]=sdom[x];else lk[x]=c[x];}for(p=1;p<=n;p++){for(int i=0;i<d[p].size();i++){int u=d[p][i];if(!idom[u]) idom[u]=idom[lk[u]];}}idom[1]=0;for(int i=1;i<=n;i++) size[i]=1;for(p=n;p>=1;p--){for(int i=0;i<d[p].size();i++){int u=d[p][i];size[idom[u]]+=size[u];}}for(int i=1;i<=n;i++) cout<<size[i]<<' ';return 0; } /*by DT_Kang*/總結(jié)
- 上一篇: 超好用的导航首页(最新)
- 下一篇: 我像TMAC?