(有些细节被 skip 了,待填)
徐老师的 blog(都是严格证明,但是菜如我的话看着可能会晕)(大雾)
考虑一张有向图 ,有起点
,满足
存在一条
到
的路径。对每个点
,我们称节点
支配节点
当且仅当任意一条
到
的路径都经过
。要对每个结点
求出有哪些节点支配它。
注意到如果节点 和
都支配
且
,那么
和
之间一定有支配关系。因此,支配的关系构成了一个有向树的结构,我们称之为支配树。
支配树可以在 时间内求出。
其中
是路径上
最浅的节点。
DAG 支配树
没什么好说的。 的 parent 是
的所有前驱在支配树上的 LCA,倍增 + DP 即可。
任意有向图支配树
以 为根先找到任意一棵 DFS 树。边被分成了几种:树边、回向边、前向边、横叉边。显然支配
的点必然是
在 DFS 树上的祖先。
令 表示
距离它最近的满足以下条件的祖先:【任意从
到
的路径都必须经过一个 从
到
的只经过树边的路径上的异于
的点】。
的一个等价定义:距离
最远的满足【存在一条从
到
只经过 DFS 先序遍历在
右侧的点的路径】的最先。这个定义可以方便求出
:考虑上一条边是不是横叉边——所有
的前驱和所有
DFS 先序比它大的前驱的祖先的
和取 min 即可。
这个过程需要一个数据结构支持加边和求到根的路径的 min。可以用带权并查集维护。
接下来我们考虑怎么用 求出
在支配树上的 parent,记为
。注意到原图在支配树意义下相当于删去所有非树边然后将
到
的树上路径向
连边。因此
就是
到
的树上路径在支配树上的 LCA。
进一步地,如果 到
上所有结点的
都是
的子孙,那么
就是
;否则,它就是
其中
是
到
的树上路径上
最浅的节点:我们要求的相当于是沿着
到
的树上路径的反方向跳必然经过的结点,而从
能跳到的点必然能从这个
跳到。
和上面使用同一个带权并查集即可。
模板题 HDU 4694
注意这题其实并不保证起点能抵达所有点:不能抵达的点答案在本题中被认为是 。
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MOD = 998244353;
int n, m, s;
bool vis[200005];
vector<int> G[200005], RG[200005];
int dfn[200005], did[200005], cnt;
int sdom[200005], idom[200005];
vector<int> T[200005];
vector<int> hv[200005];
int dpar[200005], dval[200005];
void m_dsu(int v, int u)
{
dpar[v] = u;
}
int q_dsu(int v)
{
if(dpar[v] == v) return dval[v];
int rval = q_dsu(dpar[v]);
if(sdom[dval[v]] > sdom[rval]) dval[v] = rval;
dpar[v] = dpar[dpar[v]];
return dval[v];
}
void dfs0(int v)
{
vis[v] = true;
did[v] = cnt;
dfn[cnt] = v;
cnt ++;
rep(i, G[v].size()) {
int u = G[v][i];
if(vis[u]) continue;
T[v].push_back(u);
dfs0(u);
}
}
void dfs1(int v)
{
for(int i = T[v].size() - 1; i >= 0; i --) {
int u = T[v][i];
dfs1(u);
}
sdom[v] = did[v];
rep(i, RG[v].size()) {
int u = RG[v][i];
if(!vis[u]) continue;
if(did[u] > did[v]) sdom[v] = min(sdom[v], sdom[q_dsu(u)]);
else sdom[v] = min(sdom[v], did[u]);
}
dval[v] = v;
hv[dfn[sdom[v]]].push_back(v);
rep(i, hv[v].size()) {
int u = hv[v][i], cu = q_dsu(u);
if(sdom[cu] == sdom[u]) idom[u] = sdom[u];
else idom[u] = ~cu;
}
rep(i, T[v].size()) m_dsu(T[v][i], v);
}
void dfs2(int v)
{
if(idom[v] < 0) idom[v] = idom[~idom[v]];
rep(i, T[v].size()) dfs2(T[v][i]);
}
LL dp[200005];
bool solve()
{
if(scanf("%d%d", &n, &m) != 2) return false;
rep1(i, n) {
G[i].clear();
T[i].clear();
hv[i].clear();
RG[i].clear();
}
cnt = 0;
rep(i, m) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
RG[v].push_back(u);
}
s = n;
rep1(i, n) vis[i] = false;
dfs0(s);
rep1(i, n) {
dpar[i] = i;
dval[i] = i;
}
dfs1(s);
dfs2(s);
rep1(i, n) idom[i] = dfn[idom[i]];
rep1(i, n) dp[i] = 0;
rep(i, cnt) if(i == 0) dp[dfn[i]] = n;
else dp[dfn[i]] = dp[idom[dfn[i]]] + dfn[i];
rep1(i, n) printf("%I64d%c", dp[i], " \n"[i == n]);
return true;
}
int main()
{
while(solve());
return 0;
}