[ZJOI2012]灾难

时间:2023-03-10 03:34:56
[ZJOI2012]灾难

嘟嘟嘟



偶尔翻到的一道题。



50分暴力很好想,对于每一个点进行一次拓扑排序,然后每一次别memset,只清空走过的点,能拿到70分。



正解好像也挺好想,是一个叫“灭绝树”的东西。

对于一个点\(i\),他能否被饿死由他的所有食物决定,而他的所有食物能否被饿死有这些食物的lca决定。所以这时候把lca和\(i\)连边,构成的树就叫灭绝树。答案就是每一个点的子树大小-1。

具体步骤就是先进行拓扑排序,如果一个点入度为0,那么就更新他的深度和倍增数组,并在灭绝树上连边,同时放入队尾。

然后本人因为序号重复的问题调了半天,这多半是废了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 7e4 + 5;
const int maxe = 4e6 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} int n, s, du[maxn];
vector<int> eat[maxn];
struct Edge
{
int nxt, to;
}e[maxe], e2[maxe];
int head[maxn], ecnt = -1, head2[maxn], ecnt2 = -1;
In void addEdge(int x, int y)
{
e[++ecnt] = (Edge){head[x], y};
head[x] = ecnt;
}
In void addEdge2(int x, int y)
{
e2[++ecnt2] = (Edge){head2[x], y};
head2[x] = ecnt2;
} const int N = 16;
int dep[maxn], fa[N + 2][maxn];
In ll lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(int i = N; i >= 0; --i)
if(dep[x] - (1 << i) >= dep[y]) x = fa[i][x];
if(x == y) return x;
for(int i = N; i >= 0; --i)
if(fa[i][x] ^ fa[i][y]) x = fa[i][x], y = fa[i][y];
return fa[0][x];
}
In void build_Tree()
{
queue<int> q;
for(int i = 1; i <= n; ++i) if(!du[i]) addEdge2(0, i), dep[i] = 1, q.push(i);
while(!q.empty())
{
int now = q.front(); q.pop();
for(int i = head[now], v; i != -1; i = e[i].nxt)
{
if(!--du[v = e[i].to])
{
int Lca = eat[v][0];
for(int j = 1, sz = eat[v].size(); j < sz; ++j) Lca = lca(Lca, eat[v][j]);
addEdge2(Lca, v);
fa[0][v] = Lca; dep[v] = dep[Lca] + 1;
for(int j = 1; (1 << j) <= dep[v]; ++j)
fa[j][v] = fa[j - 1][fa[j - 1][v]];
q.push(v);
}
}
}
} int sum[maxn];
In void dfs(int now)
{
sum[now] = 1;
for(int i = head2[now]; i != -1; i = e2[i].nxt)
dfs(e2[i].to), sum[now] += sum[e2[i].to];
} int main()
{
Mem(head, -1); Mem(head2, -1);
n = read();
for(int i = 1; i <= n; ++i)
{
int x = read();
while(x) ++du[i], addEdge(x, i), eat[i].push_back(x), x = read();
}
build_Tree(); dfs(0);
for(int i = 1; i <= n; ++i) write(sum[i] - 1), enter;
return 0;
}