[ZJOI2012]灾难
Description:
一个食物网有N个点,代表N种生物,如果生物x可以吃生物y,那么从y向x连一个有向边。
这个图没有环。
图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。
如果某个消费者的所有食物都灭绝了,它会跟着灭绝。
我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。
举个例子:在一个草场上,生物之间的关系是:
如果小强和阿米巴把草原上所有的羊都给吓死了,那么狼会因为没有食物而灭绝,而小强和阿米巴可以通过吃牛、牛可以通过吃草来生存下去。所以,羊的灾难值是1。但是,如果草突然灭绝,那么整个草原上的5种生物都无法幸免,所以,草的灾难值是4。
给定一个食物网,你要求出每个生物的灾难值。
Hint:
\(n \le 5*10^4\)
Solution:
不是很难的一道题,但为什么自己不往LCA那方面想呢
一个物种如果灭绝,则它的所有父亲都必须灭绝
假设我们这个点以上是一棵树,那这些父亲的LCA必须灭绝
同时把该节点给接到那个LCA上,这样一定是对的,并且维护了这棵树
按拓扑序这样逐步操作,最终形成一棵树,直接dfs算答案
要注意建新图,同时开一个虚点连接入度为0的点
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=5e4+5;
int n,cnt,tot,hd[mxn],rk[mxn],sz[mxn],in[mxn],hd2[mxn],tag[mxn],dep[mxn];
int f[mxn][19];
vector <int > fa[mxn];
queue <int > q;
inline int read() {
char c=getchar(); int x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline int chkmax(int &x,int y) {if(x<y) x=y;}
inline int chkmin(int &x,int y) {if(x>y) x=y;}
struct ed {
int to,nxt;
}t[mxn<<1],t2[mxn<<1];
inline void add(int u,int v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
inline void add2(int u,int v) {
t2[++cnt]=(ed) {v,hd2[u]}; hd2[u]=cnt;
}
void topo()
{
for(int i=1;i<=n;++i)
if(in[i]==0) q.push(i),tag[i]=1;
while(!q.empty()) {
int u=q.front(); rk[++tot]=u; q.pop();
for(int i=hd[u];i;i=t[i].nxt) {
int v=t[i].to; --in[v];
if(in[v]==0) q.push(v);
}
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;--i)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;--i)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void solve()
{
for(int i=1;i<=tot;++i) {
int w,z; w=z=rk[i];
if(tag[w]) {
add2(0,w); dep[w]=1;
continue ;
}
for(int j=0;j<fa[z].size();++j) {
int v=fa[z][j];
if(j==0) w=v;
w=lca(w,v);
}
f[z][0]=w,add2(w,z),dep[z]=dep[w]+1;
for(int j=1;j<=18;++j)
f[z][j]=f[f[z][j-1]][j-1];
}
}
void dfs(int u)
{
sz[u]=1;
for(int i=hd2[u];i;i=t2[i].nxt) {
int v=t2[i].to;
dfs(v); sz[u]+=sz[v];
}
}
int main()
{
n=read(); int x;
for(int i=1;i<=n;++i) {
while((x=read())!=0) {
add(x,i); ++in[i];
fa[i].push_back(x);
}
}
topo(); solve(); dfs(0);
for(int i=1;i<=n;++i) printf("%d\n",sz[i]-1);
return 0;
}