题目
分析
将图中所有边取反,发现要求的就是每个点支配的点数。然而发现一个问题:这个不是一个单源点的有向图。我们只需建一个源点,将其向所有入度为零的点连边,建出支配树即可。
当然,这个图是 DAG,事实上拓扑排序就 + LCA 能找到支配点。但我还是用的 LT
错因
- 没想到怎么处理多源点的 DAG 的问题;
- 板子打错了两个地方。
代码
#include <bits/stdc++.h>
const int MAXN = 65534;
int N;
int In[MAXN + 5];
std::vector<int> G[MAXN + 5], H[MAXN + 5];
std::vector<int> STree[MAXN + 5], ITree[MAXN + 5];
int DfnCnt;
int Fat[MAXN + 5];
int Dfn[MAXN + 5], Tid[MAXN + 5];
int SDom[MAXN + 5], IDom[MAXN + 5];
struct Union_Find {
int Fat[MAXN + 5], Min[MAXN + 5];
void Init(int n) {
for (int i = 1; i <= n; i++)
Fat[i] = Min[i] = i;
}
int Find(int u) {
if (Fat[u] == u)
return u;
int anc = Find(Fat[u]);
if (Dfn[SDom[Min[Fat[u]]]] < Dfn[SDom[Min[u]]])
Min[u] = Min[Fat[u]];
return Fat[u] = anc;
}
}T;
void Dfs(int u) {
Tid[Dfn[u] = ++DfnCnt] = u;
for (int i = 0; i < int(G[u].size()); i++) {
int v = G[u][i];
if (!Dfn[v])
Fat[v] = u, Dfs(v);
}
}
void Lengauer_Tarjan() {
for (int i = DfnCnt; i >= 2; i--) {
int u = Tid[i];
for (int j = 0; j < int(H[u].size()); j++) {
int v = H[u][j];
if (Dfn[v]) {
T.Find(v);
if (Dfn[SDom[T.Min[v]]] < Dfn[SDom[u]])
SDom[u] = SDom[T.Min[v]];
}
}
STree[SDom[u]].push_back(u);
int r = T.Fat[u] = Fat[u];
for (int j = 0; j < int(STree[r].size()); j++) {
int v = STree[r][j];
T.Find(v);
if (SDom[T.Min[v]] == r)
IDom[v] = r;
else
IDom[v] = T.Min[v];
}
STree[r].clear();
}
for (int i = 2; i <= DfnCnt; i++) {
int u = Tid[i];
if (IDom[u] != SDom[u])
IDom[u] = IDom[IDom[u]];
}
}
int Size[MAXN + 5];
void Count(int u) {
Size[u] = 1;
for (int i = 0; i < int(ITree[u].size()); i++) {
int v = ITree[u][i];
Count(v);
Size[u] += Size[v];
}
}
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
while (1) {
int v; scanf("%d", &v);
if (!v) break;
In[i]++;
G[v].push_back(i);
H[i].push_back(v);
}
}
for (int i = 1; i <= N; i++)
if (!In[i]) {
G[N + 1].push_back(i);
H[i].push_back(N + 1);
}
Dfs(N + 1);
for (int i = 1; i <= N + 1; i++)
SDom[i] = i;
T.Init(N + 1);
Lengauer_Tarjan();
for (int i = 1; i <= N + 1; i++)
if (IDom[i])
ITree[IDom[i]].push_back(i);
Count(N + 1);
for (int i = 1; i <= N; i++)
printf("%d\n", Size[i] - 1);
return 0;
}