P2597 [ZJOI2012]灾难(topsort+LCA)
Posted CCSU_Cola
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2597 [ZJOI2012]灾难(topsort+LCA)相关的知识,希望对你有一定的参考价值。
题意:给一张图代表食物链,图中没有环,,如果x捕食y,那么有一条有向边,由x指向y,如果某个物种的捕食对象全灭绝了,那么该物种就会灭绝,定义了一个名词“灾难值”,每个物种的灾难值为它灭绝后,会导致多少物种灭绝。输出所有1-n物种的灾难值。
思路:如果一个物种有多个捕食对象,那么该物种的所有捕食对象都灭绝了才会使得该物种灭绝,可以知道一个物种捕食对象都灭绝的条件为它所有捕食对象的最近公共祖先灭绝以及能够使得最近公共祖先灭绝的其他物种灭绝,于是可以根据这个条件重新建图,即为将一个物种挂到它所有捕食对象的LCA的下面,该步需要保证遍历到该物种的时候,它所有的子节点已经处理过了,所以需要用topsort处理。重现建图后,某个点的答案即为该点的子树大小减1,第一张给出的图需要反向建边,因为需要先处理被捕食的物种。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
int x,to;
};
node e[200010],ee[200010];
int idx=0,idxx=0,h[100010],hh[100010],depth[100010];
int d[100010],n,st[100010],fa[100010],ans[100010],anc[100010][21];
void add(int a,int b,int id){
if(id==0){
e[idx].x=b,e[idx].to=h[a],h[a]=idx++;
}
else{
ee[idxx].x=b,ee[idxx].to=hh[a],hh[a]=idxx++;
}
}
int LCA(int a,int b){
if(depth[a]<depth[b])swap(a,b);
for(int k=18;k>=0;k--){
if(depth[anc[a][k]]>=depth[b]){
a=anc[a][k];
}
}
if(a==b)return a;//其中一个为另一个的祖先
for(int k=18;k>=0;k--){
if(anc[a][k]!=anc[b][k]){
a=anc[a][k];
b=anc[b][k];
}
}
return anc[a][0];//向上第一个即为祖先
}
void dfs(int x){
ans[x]=1;
for(int i=hh[x];i!=-1;i=ee[i].to){
int j=ee[i].x;
dfs(j);
ans[x]+=ans[j];
}
}
int main(){
memset(h,-1,sizeof h);
memset(hh,-1,sizeof hh);
memset(fa,-1,sizeof fa);
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++){
while(1){
scanf("%d",&x);
if(x==0)break;
add(x,i,0);
d[i]++;
}
}
int l=1,r=0;
for(int i=1;i<=n;i++){
if(d[i]==0){
st[++r]=i;
fa[i]=0;
}
}
while(l<=r){
x=st[l++];
add(fa[x],x,1);
anc[x][0]=fa[x],depth[x]=depth[fa[x]]+1;
for(int i=1;i<=18;i++) anc[x][i]=anc[anc[x][i-1]][i-1];//更新倍增数组
for(int i=h[x];i!=-1;i=e[i].to){
int j=e[i].x;
if(fa[j]==-1)fa[j]=x;
else fa[j]=LCA(fa[j],x);
d[j]--;
if(!d[j]){
st[++r]=j;
}
}
}
dfs(0);
for(int i=1;i<=n;i++){
printf("%d\\n",ans[i]-1);
}
}
以上是关于P2597 [ZJOI2012]灾难(topsort+LCA)的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2815] [洛谷P2597] [ZJOI2012] 灾难
P2597 [ZJOI2012]灾难(topsort+LCA)