[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;
}

posted @ 2019-03-10 19:08  cloud_9  阅读(162)  评论(0编辑  收藏  举报