题目大意:
给你n(n<=5∗104)n(n<=5∗104)个点,m(m<=105)m(m<=105)条边的图,其中nn为源点,如果点到点aa的路径一定要经过点,则说明bb是的important sister。询问每个点的important sister的编号和。
分析:
一道支配树的模板题了。
某个点xx的所有支配点就是它的important sister。
支配树是一棵树,其中每个点有两个指针,分别为idomidom和sdomsdom。
idom(x)idom(x)表示xx的最近的树上支配点。因为支配具有传递性,所以显然有这样一个点。
表示不经过dfn[i]<dfn[x]dfn[i]<dfn[x]的点ii,不包括起点与终点,能到达的序最小的点。
一个结论是idom(x)idom(x)和sdom(x)sdom(x)必为xx祖先。
所以考虑递推这个东西。
如果存在一条边,那么如果dfn[y]<dfn[x]dfn[y]<dfn[x],只能走一部了,显然sdom(x)=smin(y)sdom(x)=smin(y),其中sminsmin表示取dfsdfs序小的点。
如果存在一条边(y,x)(y,x),那么如果dfn[y]>dfn[x]dfn[y]>dfn[x],则对于所有yy的祖先(包括yy),。
而对于idom(x)idom(x),如果xx到上任意一点yy(不包括),都有sdom(y)>=sdom(x)sdom(y)>=sdom(x),也就是不能通过其他边跳过sdom(x)sdom(x),那么idom(x)=sdom(x)idom(x)=sdom(x);
否则uu为该路径上的的dfsdfs序最小的点,idom(x)=idom(u)idom(x)=idom(u)。
综上,我们每次转移相当于要找一个点到祖先的路径上sdomsdom的sminsmin。所以维护一个权值并查集。sdom(x)sdom(x)直接查询他的prepre,而对于idom(x)idom(x),我们可以把某个点的挂在他的sdomsdom上,当我们枚举到他的儿子时,就把他的idomidom算一遍。因为路径上有的点的idomidom还没有计算,所以要记录转移的位置,最后再扫一遍即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#define LL long long
const int maxn=5e4+7;
const int maxe=1e5+7;
using namespace std;
int ls[maxn],dfn[maxn],id[maxn],fa[maxn],idom[maxn],sdom[maxn],p[maxn],val[maxn];
int n,m,x,y,cnt;
LL f[maxn];
struct edge{
int y,next;
}g[maxe];
vector <int> pre[maxn],dom[maxn];
void dfs(int x)
{
dfn[x]=++cnt;
id[cnt]=x;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
pre[y].push_back(x);
if (!dfn[y])
{
fa[y]=x;
dfs(y);
}
}
}
int get(int x)
{
if (p[x]==x) return x;
int y=get(p[x]);
if (dfn[sdom[val[p[x]]]]<dfn[sdom[val[x]]]) val[x]=val[p[x]];
return p[x]=y;
}
int smin(int x,int y)
{
if (dfn[x]<dfn[y]) return x;
return y;
}
void DMT()
{
for (int i=cnt;i>0;i--)
{
int x=id[i];
if (!pre[x].empty())
{
for (int j=0;j<pre[x].size();j++)
{
int y=pre[x][j];
if (dfn[y]<dfn[x]) sdom[x]=smin(sdom[x],y);
else
{
get(y);
sdom[x]=smin(sdom[x],sdom[val[y]]);
}
}
}
pre[x].clear();
p[x]=fa[x];
dom[sdom[x]].push_back(x);
if (!dom[fa[x]].empty())
{
for (int j=0;j<dom[fa[x]].size();j++)
{
int y=dom[fa[x]][j];
get(y);
int d=val[y];
if (dfn[sdom[d]]>=dfn[sdom[y]]) idom[y]=sdom[y];
else idom[y]=d;
}
dom[fa[x]].clear();
}
}
for (int i=1;i<=cnt;i++)
{
int x=id[i];
if (idom[x]!=sdom[x]) idom[x]=idom[idom[x]];
f[x]=f[idom[x]]+(LL)x;
}
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(ls,0,sizeof(ls));
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
g[i].y=y;
g[i].next=ls[x];
ls[x]=i;
}
cnt=0;
for (int i=1;i<=n;i++)
{
dfn[i]=idom[i]=0;
f[i]=0;
p[i]=val[i]=sdom[i]=i;
}
dfs(n);
DMT();
for (int i=1;i<n;i++) printf("%lld ",f[i]);
printf("%lld",f[n]);
printf("\n");
}
}