对于一张有向图,1号点出发到一个点u,有一些点是会被所有路径经过的,定义这些点支配了u。不难发现支配关系形成一棵树,树上每个点的所有祖先都支配这个点,一个点在支配树上的父亲称为它的支配点。
首先从 1 1 1开始 d f s dfs dfs,建立一颗 d f s dfs dfs树,求出每个点的 d f n dfn dfn。定义一个点 u u u半支配点 s d [ u ] sd[u] sd[u]为满足:存在一条从 s d [ u ] sd[u] sd[u]到 u u u的路径 s d [ u ] , v 1 , v 2 , . . . , v k , u sd[u],v_1,v_2,...,v_k,u sd[u],v1,v2,...,vk,u,使得 d f n [ v i ] > d f n [ u ] dfn[v_i]>dfn[u] dfn[vi]>dfn[u]的 d f n dfn dfn最小的点。设u的支配点为 i d [ u ] id[u] id[u],可以证明 i d [ u ] id[u] id[u]和 s d [ u ] sd[u] sd[u]都是 u u u在 d f s dfs dfs树上的祖先。
发现如果只有树边,那么支配树就是 d f s dfs dfs树。而每个点的半支配点最大限度地使得它的祖先不能成为它的支配点。于是对问题做一个转化:只保留 d f s dfs dfs树上的树边,再加上每一对 ( s d [ u ] , u ) (sd[u],u) (sd[u],u),新的图的支配树与原图等价。
这样对于新的图就比较好做了:首先它是一个DAG,可以直接按照DAG的做法,但还可以进一步简化。对于一个点 u u u,它的支配点是 s d [ u ] sd[u] sd[u]及其祖先中的一个。如果是 s d [ u ] sd[u] sd[u]的祖先,那么存在一条从根到 u u u跨过 s d [ u ] sd[u] sd[u]的路径,故一定有一条边 ( s d [ w ] , w ) (sd[w],w) (sd[w],w)跨过了 s d [ u ] sd[u] sd[u],直接找 u u u到 s d [ u ] 中 d f n [ s d [ w ] ] sd[u]中dfn[sd[w]] sd[u]中dfn[sd[w]]最小的 w w w即可,如果最小值为 d f n [ s d [ u ] ] dfn[sd[u]] dfn[sd[u]],则 i d [ u ] = s d [ u ] id[u]=sd[u] id[u]=sd[u]。
剩下问题在于如何找到半支配点。考虑一个点 u u u的 s d [ u ] sd[u] sd[u]的情况:可能直接是它的父亲,或者就它的一个祖先经过一些dfn大于它的点连到它。对于后者,找到这些点 d f n dfn dfn最小的点 w w w,那么 s d [ u ] sd[u] sd[u]一定支配了 w w w,且路径上所有 w w w之后的点都是 w w w的后代,可以用 w w w来更新 s d [ u ] sd[u] sd[u]。
由于 d f n [ w ] > d f n [ u ] dfn[w]>dfn[u] dfn[w]>dfn[u],于是考虑按照 d f n dfn dfn倒序。对于一个点 u u u,找到所有有边连向它的点 v v v,考虑用这些边来计算 u u u的 s d [ u ] sd[u] sd[u]。如果 d f n [ v ] < d f n [ u ] dfn[v]dfn[v]<dfn[u],直接用 v v v更新 s d [ u ] sd[u] sd[u],否则,由于 w w w一定是 v v v的祖先,找到 v v v到根的 s d [ u ] sd[u] sd[u]的最小值即可。之后,把 u u u连向它 d f s dfs dfs树上的父亲,可计算出所有 s d [ u ] = f a [ u ] sd[u]=fa[u] sd[u]=fa[u]的点的 i d [ x ] id[x] id[x]。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,hd[N],to[N],nx[N],tt;
void add(int u,int v){ //cout<<"add:"<
nx[++tt]=hd[u]; to[hd[u]=tt]=v;
}
vector<int>fr[N],ux[N];
int dn[N],fa[N],dx[N],sz[N],ct;
void dfs(int u){
dn[u]=++ct; dx[ct]=u; sz[u]=1;
for(int e=hd[u];e;e=nx[e]) if(!dn[to[e]]){
int v=to[e]; fa[v]=u; dfs(v); sz[u]+=sz[v];
}
}
int id[N],sd[N],mn[N],fu[N],su[N],pt[N];
int find(int u){
if(u==fu[u]) return u;
int v=fu[u],w=find(fu[u]);
if(mn[v]<mn[u]) pt[u]=pt[v],mn[u]=mn[v];
fu[u]=w;
return w;
}
vector<int>h[N]; bool vs[N];
int main()
{
srand(time(0));
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m;
for(int u,v,i=1;i<=m;i++)
scanf("%d%d",&u,&v),h[u].push_back(v),fr[v].push_back(u);
for(int i=1;i<=n;i++){
sort(h[i].begin(),h[i].end());
int z=unique(h[i].begin(),h[i].end())-h[i].begin();
for(int j=0;j<z;j++) add(i,h[i][j]);
}
dfs(1);
for(int i=1;i<=n;i++) sd[i]=fu[i]=i,pt[i]=i,mn[i]=dn[i];//cout<<"u="<for(int i=n;i;i--){
int u=dx[i],z=fr[u].size();
//cout<<"u="<
for(int j=0;j<z;j++){
int v=fr[u][j];
if(dn[v]<dn[u]){
if(dn[v]<dn[sd[u]]) sd[u]=v;
}
else{
find(v);
if(mn[v]<dn[sd[u]]) sd[u]=dx[mn[v]];
}
}
//cout<<"sd="<
int v=fa[u]; ux[sd[u]].push_back(i);
fu[u]=v;
if(dn[sd[v]]<dn[sd[u]]) mn[u]=dn[sd[v]],pt[u]=v;
else mn[u]=dn[sd[u]],pt[u]=u;
int b=dn[u]+sz[u]-1; z=ux[v].size();
if(!z) continue;
int l=0,r=z-1;
while(l<r){
int mid=(l+r)>>1;
if(ux[v][mid]<=b) r=mid;
else l=mid+1;
}
if(ux[v][l]>b) l++;
for(int j=l;j<z;j++){
int x=dx[ux[v][j]];
find(x);
if(mn[x]==dn[sd[x]]) id[x]=sd[x];
else id[x]=pt[x],vs[x]=1;
}
}
for(int i=1;i<=n;i++) if(vs[dx[i]]) id[dx[i]]=id[id[dx[i]]];
for(int i=n;i;i--) su[dx[i]]++,su[id[dx[i]]]+=su[dx[i]];
for(int i=1;i<=n;i++) printf("%d ",su[i]); puts("");
return 0;
}