日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【支配树模板】

發布時間:2023/12/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【支配树模板】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

支配樹模板題

注:本文均從網絡上摘抄

首先介紹一下什么叫支配樹。
1.支配點:
在有向圖中,若刪除了點x,u到v不連通了,那么稱x支配v。
2.支配樹:
滿足樹上一個點x的所有祖先都是它的支配點的樹。

下面介紹一下一般有向圖的支配樹構建方法:
下面化還是一些名詞解釋:
1.dfs樹:
從圖的一個點S開始dfs整張圖,可以提取出一顆dfs樹,并且記x的dfs序為dfn[x]。
2.半支配點:
假設存在一個點y,從y出發有一條到x的路徑,并且路徑上任意一點z(z!=x && z!=y)都滿足dfn[z]>dfn[x]dfn[z]>dfn[x]dfn[z]>dfn[x],則稱y為x的半支配點。
semi[x]semi[x]semi[x]為x的dfn最小的半支配點,因為x在dfs樹上的父親也是它的一個半支配點,所以semi[x]semi[x]semi[x]一定是x的祖先。并且刪掉原圖中的非樹邊后,連邊(semi[x],x),不改變原圖中的支配點關系。
求出了semi,我們就把圖變成了一個DAG,然后就可以重復DAG的做法了(不過我沒有講DAG的做法qwq),下面介紹一種更優的做法。

求半支配點:
對于一個點x,我們找到所有邊(y,x)對應的y。
dfn[y]<dfn[x]dfn[y]<dfn[x]dfn[y]<dfn[x]dfn[y]dfn[y]dfn[y]比當前找到的semi[x]的dfn小,則semi[x]=ysemi[x] = ysemi[x]=y
dfn[y]>dfn[x]dfn[y]>dfn[x]dfn[y]>dfn[x],找到樹上y的一個祖先z,且dfn[z]>dfn[x]dfn[z]>dfn[x]dfn[z]>dfn[x],比較dfn[semi[z]]dfn[semi[z]]dfn[semi[z]]dfn[semi[x]]dfn[semi[x]]dfn[semi[x]]的大小,決定是否用semi[z]semi[z]semi[z]更新semi[x]semi[x]semi[x]

從半支配點到支配點:
對于x,我們要求它在支配樹上的父親,也就是idom[x]idom[x]idom[x]
方法如下:
記P為從semi[x]semi[x]semi[x]到x的樹上路徑點集(不包括semi[x]),而z是P中dfn[semi[z]]dfn[semi[z]]dfn[semi[z]]最小的點。若semi[z]==semi[x]semi[z]==semi[x]semi[z]==semi[x],則有idom[x]=semi[x]idom[x]=semi[x]idom[x]=semi[x],否則有idom[x]=idom[z]idom[x] = idom[z]idom[x]=idom[z]

算法流程:
帶權并查集實現即可。

#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+7; int n,m,cnt = 0; int p[maxn],val[maxn],sdom[maxn],idom[maxn],ans[maxn]; int dfn[maxn],id[maxn],fa[maxn]; vector<int> nxt[maxn],pre[maxn],lat[maxn],zps[maxn]; void dfs(int now) {dfn[now] = ++cnt;id[cnt] = now;for(int i=0;i<nxt[now].size();i++){int v = nxt[now][i];if(dfn[v]) continue;dfs(v);fa[v] = now;} } int find(int x) {if(x==p[x]) return x;int rt = find(p[x]);if(dfn[sdom[val[p[x]]]]<dfn[sdom[val[x]]]) val[x] = val[p[x]];return p[x] = rt; } void tarjan() {for(int i=cnt;i>=2;i--){int now = id[i];for(int j=0;j<pre[now].size();j++){int v = pre[now][j];if(!dfn[v]) continue;find(v);if(dfn[sdom[val[v]]]<dfn[sdom[now]]) sdom[now] = sdom[val[v]];}lat[sdom[now]].push_back(now);p[now] = fa[now];now = fa[now];for(int j=0;j<lat[now].size();j++){int v = lat[now][j];find(v);if(sdom[val[v]]==now) idom[v] = now;else idom[v] = val[v];}}for(int i=2;i<=cnt;i++){int now = id[i];if(idom[now]!=sdom[now]) idom[now] = idom[idom[now]];} } void gao(int now) {ans[now] = 1;for(int i=0;i<zps[now].size();i++){int v = zps[now][i];gao(v);ans[now] += ans[v];} } int main() {scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);nxt[u].push_back(v);pre[v].push_back(u);}for(int i=1;i<=n;i++){sdom[i] = p[i] = val[i] = i;}dfs(1);tarjan();for(int i=2;i<=n;i++){if(idom[i]) zps[idom[i]].push_back(i);}gao(1);for(int i=1;i<=n;i++) printf("%d ",ans[i]);puts("");return 0; }

總結

以上是生活随笔為你收集整理的【支配树模板】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。