P5180-[模板]支配树
正題
題目鏈接:https://www.luogu.com.cn/problem/P5180
題目大意
給出nnn個點的一張有向圖,求每個點支配的點數量。
1≤n≤2×105,1≤m≤3×1051\leq n\leq 2\times 10^5,1\leq m\leq 3\times 10^51≤n≤2×105,1≤m≤3×105
解題思路
首先定義半支配點semixsemi_xsemix?表示對于點xxx尋找一個dfndfndfn序最小的點yyy滿足存在一條yyy到xxx的路徑去掉頭尾之后所有點的dfndfndfn序都大于xxx的。
考慮怎么求每個點的半支配點,考慮兩種情況對于一個能夠直接到達xxx的點yyy
主要是第二種情況我們相當于要找一個在某個點到根節點路徑上的點使得它的半支配點dfndfndfn序最小。
那么可以考慮倒序枚舉,然后用帶權并查集維護那個半支配點編號最小的。
之后就是半支配點有什么用,大概就是半支配點向點連邊那么新的圖支配關系不變。
所以一種暴力的做法就是直接跑DAGDAGDAG的支配樹求法,但是有更快的。
考慮對于一個點xxx和它的半支配點yyy,如果yyy到xxx的路徑上我們找到一個半支配點dfndfndfn序最小的節點uuu且它的半支配點為vvv。
那么如果
這個過程中uuu和vvv的維護和上面一樣,所以可以一起求解。
但是我們可以暫時不知道uuu的支配點,所以可以先記錄,最后在正序的記回去。
時間復雜度O(nα(n))O(n\alpha(n))O(nα(n))
code
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int N=2e5+10; int n,m,cnt,dfn[N],rfn[N],anc[N],ans[N]; int semi[N],idom[N],pt[N],fa[N]; vector<int>G[N],D[N],T[N]; void tarjan(int x){dfn[x]=++cnt;rfn[cnt]=x;for(int i=0;i<G[x].size();i++){int y=G[x][i];if(!dfn[y])anc[y]=x,tarjan(y);}return; } int find(int x){if(fa[x]==x)return x;int ans=find(fa[x]);if(dfn[semi[pt[fa[x]]]]<dfn[semi[pt[x]]])pt[x]=pt[fa[x]];return (fa[x]=ans); } void GetIdom(){for(int p=n;p>=2;p--){int x=rfn[p];for(int i=0;i<D[x].size();i++){int y=D[x][i];if(!dfn[y])continue;find(y);if(dfn[semi[pt[y]]]<dfn[semi[x]])semi[x]=semi[pt[y]];}fa[x]=anc[x];T[semi[x]].push_back(x);x=anc[x];for(int i=0;i<T[x].size();i++){int y=T[x][i];find(y);idom[y]=(semi[pt[y]]==x)?x:pt[y];}T[x].clear();}for(int i=2;i<=n;i++){int x=rfn[i];if(idom[x]!=semi[x])idom[x]=idom[idom[x]];}return; } int main() {scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);G[x].push_back(y);D[y].push_back(x);}for(int i=1;i<=n;i++)semi[i]=fa[i]=pt[i]=i;tarjan(1);GetIdom();for(int i=n;i>=1;i--){int x=rfn[i];ans[x]++;ans[idom[x]]+=ans[x];}for(int i=1;i<=n;i++)printf("%d ",ans[i]);return 0; }總結
以上是生活随笔為你收集整理的P5180-[模板]支配树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑键盘F1到F12的正确用法如何用键盘
- 下一篇: P4428-[BJOI2018]二进制【