正题
题目链接: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
- dfny<dfnxdfn_y<dfn_xdfny<dfnx:那么yyy可能是xxx的半支配点
- dfny>dfnxdfn_y>dfn_xdfny>dfnx:那么设vvv表示yyy到dfsdfsdfs根节点的路径上的某个点uuu的dfndfndfn序最小的半支配点,那么vvv可能是uuu的半支配点
主要是第二种情况我们相当于要找一个在某个点到根节点路径上的点使得它的半支配点dfndfndfn序最小。
那么可以考虑倒序枚举,然后用带权并查集维护那个半支配点编号最小的。
之后就是半支配点有什么用,大概就是半支配点向点连边那么新的图支配关系不变。
所以一种暴力的做法就是直接跑DAGDAGDAG的支配树求法,但是有更快的。
考虑对于一个点xxx和它的半支配点yyy,如果yyy到xxx的路径上我们找到一个半支配点dfndfndfn序最小的节点uuu且它的半支配点为vvv。
那么如果
- v=yv=yv=y,那么证明整条路径上没有dfndfndfn序更小的半支配点,yyy就是xxx的支配点。
- du>dyd_u>d_ydu>dy,那么显然uuu有更小的支配点支配这套路径,所以uuu的支配点就是yyy的支配点
这个过程中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;
}