[ZJOI2012]灾难

题目描述

阿米巴是小强的好朋友。

阿米巴和小强在草原上捉蚂蚱。小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难。

学过生物的阿米巴告诉小强,草原是一个极其稳定的生态系统。如果蚂蚱灭绝了,小鸟照样可以吃别的虫子,所以一个物种的灭绝并不一定会引发重大的灾难。

我们现在从专业一点的角度来看这个问题。我们用一种叫做食物网的有向图来描述生物之间的关系:

一个食物网有N个点,代表N种生物,如果生物x可以吃生物y,那么从y向x连一个有向边。

这个图没有环。

图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。

如果某个消费者的所有食物都灭绝了,它会跟着灭绝。

我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。

举个例子:在一个草场上,生物之间的关系是:

如果小强和阿米巴把草原上所有的羊都给吓死了,那么狼会因为没有食物而灭绝,而小强和阿米巴可以通过吃牛、牛可以通过吃草来生存下去。所以,羊的灾难值是1。但是,如果草突然灭绝,那么整个草原上的5种生物都无法幸免,所以,草的灾难值是4。

给定一个食物网,你要求出每个生物的灾难值。

输入格式

输入文件 catas.in 的第一行是一个正整数 N,表示生物的种数。生物从 1 标

号到 N。

接下来 N 行,每行描述了一个生物可以吃的其他生物的列表,格式为用空

格隔开的若干个数字,每个数字表示一种生物的标号,最后一个数字是 0 表示列

表的结束。

输出格式

输出文件catas.out包含N行,每行一个整数,表示每个生物的灾难值。

输入输出样例
输入

5
0
1 0
1 0
2 3 0
2 0

输出

4
1
0
0
0

说明/提示
【样例说明】

样例输入描述了题目描述中举的例子。

【数据规模】

对50%的数据,N ≤ 10000。

对100%的数据,1 ≤ N ≤ 65534。

输入文件的大小不超过1M。保证输入的食物网没有环。

题解

这题我搞了差不多两三天吧,我先是去搞了一下支配树(显而易见没有搞懂)

然后我发现这题不用搞支配树呀。。

运用支配树的思想,并拓扑排序,这样的话当你把拓扑排序后的前 x − 1 x-1 x1个点加入支配树后,指向第 x x x个点的所有点就都在支配树里了,那么新加进来的这个点就应该被加到所有指向他的点的 l c a lca lca的儿子中,因为只有当所有指向他的点都灭绝了他才会灭绝,而能够使他直接灭绝的点就是所有指向他的点的 l c a lca lca

然后搞出支配树之后,能够支配这个点的点集就是它的祖先,被这个点支配的点集就是它的子树。

代码并不难理解,也很好打:

PS:
支配树,顾名思义,就是棵树,树上每个节点的父亲就是他的最近支配点(离他最近的支配点)

# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <set>
# include <map>
# include <queue>
# include <stack>
# include <cctype>
# include <cstdlib>
using namespace std;
namespace fastio{
	template<typename tn> void read(tn &a){
	    tn x=0,f=1;char c=' ';
	    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	    for(;isdigit(c);c=getchar() ) x=x*10+c-'0';
	    a=x*f;
	}
	template<typename tn> void print(tn a){
	    if(a<0) putchar('-'),a=-a;
	    if(a>9) print(a/10);
	    putchar(a%10+'0');
	}
};
using namespace fastio;
const int N=7e4+5;
vector<int> son[N],fa[N];
int f[N][20],d[N],lg[N];
struct Edge{
	int nex,to;
}e[N];
int rd[N],head[N],cnt;
void add(int x,int y){
	e[++cnt].nex=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
int n;
queue<int> q;
void update(int x,int fa){
	d[x]=d[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
	return;
}
int lca(int x,int y){
	if(d[x]<d[y]) swap(x,y);
	while(d[x]>d[y]) x=f[x][lg[d[x]-d[y]]-1];
	if(x==y) return x;
	for(int k=lg[d[x]]-1;k>=0;k--)
      	if(f[x][k]!=f[y][k])
        	x=f[x][k],y=f[y][k];
    return f[x][0];
}
int siz[N];
void dfs(int x){
	siz[x]=1;
	for(int i=head[x];i;i=e[i].nex)
		dfs(e[i].to),siz[x]+=siz[e[i].to];
}
int main(){
	read(n);
	for(int i=1;i<=n;i++)
      	lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(int i=1;i<=n;i++){
		int x;
		while("xyclovesqyn"){
			read(x);
			if(x==0) break;
			son[x].push_back(i);
			fa[i].push_back(x); //lippahha depil
			rd[i]++;
		}
	}
	for(int i=1;i<=n;i++)
		if(rd[i]==0) q.push(i),add(0,i),d[i]=1;
	while(q.size()){
		int x=q.front();
		q.pop();
		int xyc=son[x].size();
		for(int i=0;i<xyc;i++){
			int y=son[x][i];
			rd[y]--;
			if(!rd[y]){
				int qyn=fa[y].size();
				int lav=fa[y][0];
				for(int j=1;j<qyn;j++) lav=lca(lav,fa[y][j]);
				add(lav,y);
				update(y,lav);
				q.push(y);
			}
		}
	}
	dfs(0);
	for(int i=1;i<=n;i++) print(siz[i]-1),puts("");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值