题目链接:https://www.luogu.org/problem/P2597
题意:
给你食物链中所有生物的食物(有向无环图DAG),现在问,如果每个生物i消失了会有多少生物也随之消失。
做法:
神奇的做法
我们看下面的两个图,右边是按照题意给你的捕食和被捕食关系建立的有向图,左边是我们要用来查询LCA的图。这是发生了什么呢。我们先根据右边的图进行拓扑得到拓扑序,拓扑序越大的数就越有可能是底层被捕食的生物,所以我们反向来建立左边的那张图。我们可以知道,如果一个生物x是我们现在枚举的生物y所捕食生物的终端,即如果x死掉了,那么y所吃的所有生物也就死掉了,那么y一定会死,这里的x就是y的支配点。
那么,我们就将y连接到其深度最大的支配点上,即lca最近公共祖先,可以想象,如果x也有支配点z,那么z如果消失y也会死。但是如果一开始就是食物链最低端或者是某一生物只缺少一个食物并没有消失的话那怎么办呢,所以我们要建立一个虚点0,让所有这样的生物都连接到0点上(下面的图中点1应该是连接到0上的),注意lca的实时更新,这样我们得出左边的图之后只要做一遍dfs,统计其有几个儿子结点即可。
代码是自己敲的可能有点丑陋。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=70005;
const int maxm=1000005;
int to[maxm],nex[maxm],head[maxn],cnt;
int n,m,dfn[maxn],id[maxn],inde[maxn];
int fa[maxn][21],ans[maxn],tot,dep[maxn];
queue<int> Q;
vector<int> ve[maxn];
void add(int u,int v){
//printf("from = %d to = %d\n",u,v);
to[cnt]=v,nex[cnt]=head[u];
head[u]=cnt++;
}
void solve(int x){
for(int j=1;(1<<j)<=n;j++)
fa[x][j]=fa[fa[x][j-1]][j-1];
}
int LCA(int x,int y){
if(x==y) return x;
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int dfs(int u){
ans[u]=0;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
ans[u]+=dfs(v)+1;
}
return ans[u];
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
while(x!=0){
add(i,x);
inde[x]++;
scanf("%d",&x);
}
}
rep(i,1,n){
if(inde[i]==0) {
Q.push(i);
}
}
while(!Q.empty()){
int u=Q.front(); Q.pop();
dfn[u]=++tot,id[tot]=u;
for(int i=head[u];~i;i=nex[i]){
int v=to[i];
if(--inde[v]==0) Q.push(v);
}
}
for(int i=tot;i>=1;i--){
int u=id[i];
if(head[u]==-1) {
ve[0].push_back(u);
dep[u]=1;
continue;
}
int v=to[head[u]];
for(int i=head[u];~i;i=nex[i]){
int tv=to[i];
v=LCA(v,tv);
}
fa[u][0]=v;
dep[u]=dep[v]+1;
solve(u);
ve[v].push_back(u);
//printf("from = %d to = %d\n",v,u);
}
dfs(0);
rep(i,1,n){
printf("%d\n",ans[i]);
}
return 0;
}