#2194. 大灾难(catas)

题目描述

在一个生态圈中,食物链的维系是很重要的。食物链的断裂往往引起连锁反应,进而招致生态系统如同多米诺骨牌一样坍塌。

现在考虑一个简化模型。在一个生态系统中,有N 种生物,它们分为两类:生产者与消费者。生产者通过这个系统之外的能量来生存,最常见的是植物的光合作用。而消费者需要“消费”,也就是以其他生物为食物才可以生存。为了简化问题,我们假设所有消费者是可以分层的,高一层的消费者可能的食物来源都来自它的严格下层。生产者可以视为最下层的成员。

当一种生物灭绝之后,依赖于它的消费者会由于所有食物消失而灭绝。

而这些消费者的灭绝可能进一步引起它的上层成员灭绝。我们定义指标C(x),用于表示当x 从生态系统中消失之后,随它灭绝的生物数量。给定一个食物链模型,对所有生物,计算它的灭绝指标C(x)。

输入格式

第一行一个整数N,表示生物的数量。生物从1 开始编号。

接下来N 行,第i 行表示第i 种生物的食物列表。两种食物的编号以空格隔开,输入0 表示本行结束。如果这一行只有一个0,这是一个生产者,不能认为它没有食物而天然灭绝。

输出格式

共N 行,第i 行是第i 个生物的灭绝指标C(i)。

样例
样例输入

5
0
1 0
1 0
2 3 0
2 0

样例输出

4
1
0
0
0

数据范围与提示

【样例解释】
1 号生物是唯一的生产者。它的灭绝将导致所有生物灭绝。
2 号与3 号生物位于食物链的次下层。2 号的灭绝将导致5 号的所有食物消失从而灭绝,但不会有其他效果。
4 号与5 号位于食物链的最上层。它们的灭绝不会改变食物链下层的格局。

【数据范围】
对于50% 的数据,N <= 10000
对于100% 的数据,N <= 65534,输入保证合法(即可以分层)
输入文件大小不超过1MB

来源

2015福建冬令营day4
显示分类标签
题解:
首先,可知当且仅当所有食物全部灭绝时,当前的生物才会灭绝。
假如我们知道当哪个生物灭绝时,所有食物都会全部灭绝,那当前生物对能让这个生物灭绝的都有贡献,所以考虑维护一个灭绝树。
把当前生物的父亲为灭绝树中所有食物的LCA,表示只有父亲灭绝他才能灭绝。
建树的顺序按拓扑序。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define N 70005
using namespace std;
int head[N],ver[N<<3],nex[N<<3];
int tot;int T,f[N][20],ru[N],dep[N];
int vh[N],vv[N<<2],vn[N<<2],vt,size[N],n;
vector<int> food[N];
inline int read()
{
	char $;int u;
	while(($=getchar())>57||$<48);u=$-'0';
	while(($=getchar())>47&&$<58)u+=u+(u<<3)+$-48;
	return u;
}
inline void add(int x,int y){
	nex[++tot]=head[x];head[x]=tot;ver[tot]=y;ru[y]++;
}

inline void v_add(int x,int y){
	vn[++vt]=vh[x];vh[x]=vt;vv[vt]=y;
}
int get_lca(int x,int y){
	if(dep[x]>dep[y])swap(x,y);
	for(int i=T;i+1;--i)if(dep[f[y][i]]>=dep[x])y=f[y][i];
    if(x==y)return x;
    for(int i=T;i+1;--i)if(f[y][i]!=f[x][i])y=f[y][i],x=f[x][i];
    return f[x][0];
}
void Link(int x){
	int lca=food[x][0];
	for(int i=1;i<food[x].size();++i){
		lca=get_lca(lca,food[x][i]);
	}
	f[x][0]=lca;dep[x]=dep[lca]+1;
	v_add(lca,x);
//	cout<<x<<" "<<lca<<endl;
	for(int i=1;i<=T;++i){
		f[x][i]=f[f[x][i-1]][i-1];
	}
}

void toposort(){
	queue<int> q;
	q.push(n+1);dep[n+1]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		if(x!=n+1)Link(x);
		for(int i=head[x];i;i=nex[i]){
			ru[ver[i]]--;
			if(!ru[ver[i]])q.push(ver[i]);
		}
	}
}
void dfs(int x){
	size[x]=1;
	for(int i=vh[x];i;i=vn[i]){
		int y=vv[i];
		dfs(y);
		size[x]+=size[y];
	}
}
int main(){
	n=read();
	for(int i=1;i<=n;++i){
		int x=0;x=read();
		while(x!=0){
			food[i].push_back(x);
			x=read();
		}
		if(food[i].empty())food[i].push_back(n+1);
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<food[i].size();++j){
			add(food[i][j],i);
		}
	}

	T=(double)log(n*1.0)/log(2);
	toposort();
	dfs(n+1);
	for(int i=1;i<=n;++i)cout<<size[i]-1<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值