题目大意:
给你
n(n<=5∗104)
n
(
n
<=
5
∗
10
4
)
个点,
m(m<=105)
m
(
m
<=
10
5
)
条边的图,其中
n
n
为源点,如果点到点
a
a
的路径一定要经过点,则说明
b
b
是的important sister。询问每个点的important sister的编号和。
分析:
一道支配树的模板题了。
某个点
x
x
的所有支配点就是它的important sister。
支配树是一棵树,其中每个点有两个指针,分别为
idom
i
d
o
m
和
sdom
s
d
o
m
。
idom(x)
i
d
o
m
(
x
)
表示
x
x
的最近的树上支配点。因为支配具有传递性,所以显然有这样一个点。
表示不经过
dfn[i]<dfn[x]
d
f
n
[
i
]
<
d
f
n
[
x
]
的点
i
i
,不包括起点与终点,能到达的序最小的点。
一个结论是
idom(x)
i
d
o
m
(
x
)
和
sdom(x)
s
d
o
m
(
x
)
必为
x
x
祖先。
所以考虑递推这个东西。
如果存在一条边,那么如果
dfn[y]<dfn[x]
d
f
n
[
y
]
<
d
f
n
[
x
]
,只能走一部了,显然
sdom(x)=smin(y)
s
d
o
m
(
x
)
=
s
m
i
n
(
y
)
,其中
smin
s
m
i
n
表示取
dfs
d
f
s
序小的点。
如果存在一条边
(y,x)
(
y
,
x
)
,那么如果
dfn[y]>dfn[x]
d
f
n
[
y
]
>
d
f
n
[
x
]
,则对于所有
y
y
的祖先(包括
y
y
),。
而对于
idom(x)
i
d
o
m
(
x
)
,如果
x
x
到上任意一点
y
y
(不包括),都有
sdom(y)>=sdom(x)
s
d
o
m
(
y
)
>=
s
d
o
m
(
x
)
,也就是不能通过其他边跳过
sdom(x)
s
d
o
m
(
x
)
,那么
idom(x)=sdom(x)
i
d
o
m
(
x
)
=
s
d
o
m
(
x
)
;
否则
u
u
为该路径上的的
dfs
d
f
s
序最小的点,
idom(x)=idom(u)
i
d
o
m
(
x
)
=
i
d
o
m
(
u
)
。
综上,我们每次转移相当于要找一个点到祖先的路径上 sdom s d o m 的 smin s m i n 。所以维护一个权值并查集。 sdom(x) s d o m ( x ) 直接查询他的 pre p r e ,而对于 idom(x) i d o m ( x ) ,我们可以把某个点的挂在他的 sdom s d o m 上,当我们枚举到他的儿子时,就把他的 idom i d o m 算一遍。因为路径上有的点的 idom i d o m 还没有计算,所以要记录转移的位置,最后再扫一遍即可。
代码:
#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");
}
}