前言(传送门)
这是一道神奇的生物题,我和同桌讨论了一节课,然后想到了一些和正解接近的东西,但还是没想到具体的做法。在看完题解后,感觉这题并不难,但是有点神奇。
题解
首先,食物网是个DAG,我们将食物连向捕食者,并建立虚点表示太阳,并连向所有的生产者。此时基图连通。
拓扑排序,我们想知道生物x的灾难值,但我们是从食物开始做的,所以考虑哪些生物死了会导致x死掉。一种生物的死亡必然是其所有食物死亡导致的,而食物也要吃其他生物。于是这是一个依赖结构。
我们考虑对原图建树,树上的每个节点死掉儿子及后代都会死。那x该连向谁呢?明显是其所有食物的LCA,x死(被一种生物影响)就是LCA或其祖先死,等价于LCA死。
充分性:x所有食物的LCA一死,食物全死,x就没饭吃了,死。
必要性:若LCA活着,则必然有食物没死,x还能吃,所以LCA必死。
我们Topo时食物已经从树上向下连好边了,于是直接动态求所有食物的LCA即可。由于插入x只改变x的信息,直接计算其dep和倍增数组就行了。然后由LCA向x连边。
最后连出一棵“灭绝树”,每个点灭绝,子树全部灭绝。于是每种生物的灾难值就是其子树大小-1,一次DFS就出来了。(洛谷题解中所谓的求树上前缀和就是计算这个)
由于读入不超过1M,所以跑得过。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define maxn 66666
#define Lg 20
using namespace std;
int n, cur = -1;
struct List{
int obj;
List *next;
}*head[maxn], Edg[maxn];
vector <int> P[maxn], food[maxn];
queue <int> Q;
void Addedge(int a, int b){
Edg[++cur].next = head[a];
Edg[cur].obj = b;
head[a] = Edg+cur;
}
int in[maxn], f[Lg][maxn], dep[maxn], sum[maxn];
int LCA(int x, int y){
if(dep[x] > dep[y]) swap(x, y);
for(int i = 18; i >= 0; i--)
if(dep[f[i][y]] >= dep[x])
y = f[i][y];
if(x == y) return x;
for(int i = 18; i >= 0; i--)
if(f[i][x] != f[i][y]){
x = f[i][x];
y = f[i][y];
}
return f[0][x];
}
void Topo(){
for(int i = 1; i <= n; i++){
if(!in[i]){
in[i] = 1;
P[0].push_back(i);
food[i].push_back(0);
}
}
Q.push(0);
dep[0] = 1;
for(int i = 0; i <= 18; i++)
f[i][0] = 0;
for(int i = 0; i <= n; i++) head[i] = NULL;
while(!Q.empty()){
int now = Q.front();
Q.pop();
int siz1 = P[now].size();
for(int i = 0; i < siz1; i++){
int v = P[now][i];
in[v] --;
if(!in[v]){
Q.push(v);
int lca = now;
int siz2 = food[v].size();
for(int j = 0; j < siz2; j++)
lca = LCA(lca, food[v][j]);
Addedge(lca, v);
f[0][v] = lca;
dep[v] = dep[lca] + 1;
for(int j = 1; j <= 18; j++)
f[j][v] = f[j-1][f[j-1][v]];
}
}
}
}
void Dfs(int x){
sum[x] = 1;
for(List *p = head[x]; p; p = p->next){
int v = p->obj;
Dfs(v);
sum[x] += sum[v];
}
}
int main(){
scanf("%d", &n);
int x;
for(int i = 1; i <= n; i++){
while(~ scanf("%d", &x) && x){
P[x].push_back(i);
food[i].push_back(x);
in[i] ++;
}
}
Topo();
Dfs(0);
for(int i = 1; i <= n; i++)
printf("%d\n", sum[i] - 1);
return 0;
}