您的位置:首页 > 其它

[BZOJ2815][ZJOI2012]灾难(拓扑排序+LCA)

2017-11-19 11:38 387 查看
可以想到建一棵灭绝树(森林),也就是说一个节点灭绝后它的子树灭绝。怎么建呢?

由于原图没有环,所以可以想到根据拓扑序建树。对于没有入度的点,直接作为根节点,对于剩下的点,假设现在考虑到了点u,并且点u之前的灭绝树已经建好,此时考虑点u应该作为哪个节点的子节点。可以想到,节点u灭绝,当且仅当节点u的所有食物的LCA灭绝,所以,求出节点u的所有食物的LCAv之后,就可以把v作为u的父亲,并初始化u的倍增数组(倍增LCA支持添加叶子节点)。

建树后,每个节点的size减1就是该节点的灾难值。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 7e4 + 5, M = 1e6 + 5, LogN = 22;
int n, ecnt, nxt[M], adj
, go[M], ecnt2, nxt2[M], adj2
, go2[M],
ecnt3, nxt3[M], adj3
, go3[M], dep
, fa
[LogN], H, T, Q[M], cnt
,
cnt2
, sze
;
void add_edge(int u, int v) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
void add_edge2(int u, int v) {
nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
void add_edge3(int u, int v) {
nxt3[++ecnt3] = adj3[u]; adj3[u] = ecnt3; go3[ecnt3] = v;
}
int lca(int u, int v) {
int i, x; if (dep[u] < dep[v]) swap(u, v);
x = dep[u] - dep[v];
for (i = 20; i >= 0; i--)
if ((x >> i) & 1) u = fa[u][i];
if (u == v) return u;
for (i = 20; i >= 0; i--)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
void topo() {
int i; H = T = 0;
for (i = 1; i <= n; i++) if (!cnt[i]) Q[++T] = i;
while (H < T) {
int u = Q[++H], tmp = go2[adj2[u]];
for (int e = adj2[u]; e; e = nxt2[e])
tmp = lca(tmp, go2[e]); dep[u] = dep[fa[u][0] = tmp] + 1;
for (i = 0; i <= 19; i++)
fa[u][i + 1] = fa[fa[u][i]][i];
if (tmp) add_edge3(tmp, u), cnt2[u]++;
for (int e = adj[u], v; e; e = nxt[e])
if (!(--cnt[v = go[e]])) Q[++T] = v;
}
}
void dfs2(int u) {
sze[u] = 1; for (int e = adj3[u]; e; e = nxt3[e])
dfs2(go3[e]), sze[u] += sze[go3[e]];
}
void solve() {
topo(); int i; for (i = 1; i <= n; i++)
if (!cnt2[i]) dfs2(i);
for (i = 1; i <= n; i++) printf("%d\n", sze[i] - 1);
}
int main() {
int i, x; n = read();
for (i = 1; i <= n; i++)
while (x = read()) add_edge(x, i), add_edge2(i, x),
cnt[i]++; solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: