[支配树] Bzoj P2815 灾难

题目描述

阿米巴是小强的好朋友。

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

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

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

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

这个图没有环。

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

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

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

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

如 

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

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

输入输出格式

输入格式:

 

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

号到 N。

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

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

表的结束。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 
5
0
1 0
1 0
2 3 0
2 0
输出样例#1: 
4
1
0
0
0

说明

【样例说明】

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

【数据规模】

对50%的数据,N ≤ 10000。

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

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

 

 

题解

  • 直接把支配树建出来,然后一个点的答案就是它在支配树上的size-1

代码

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 using namespace std;
 6 const int N=100010;
 7 int n,cnt,last[N],head[N],a[N],fa[N][20],deep[N],d[N],size[N];
 8 struct edge{int to,from;}e[N*30];
 9 queue<int>Q;
10 void insert(int x,int y)
11 {
12     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
13     e[++cnt].to=x,e[cnt].from=last[y],last[y]=cnt;
14 }
15 void topusort()
16 {
17     Q.push(n+1);
18     while (!Q.empty())    
19     {
20         int u=Q.front(); Q.pop(),a[++a[0]]=u;
21         for (int i=head[u];i;i=e[i].from)
22         {
23             d[e[i].to]--;
24             if (!d[e[i].to]) Q.push(e[i].to);
25         }
26     }
27 }
28 int getlca(int x,int y)
29 {
30     if (deep[x]<deep[y]) swap(x,y);
31     for (int i=16;i>=0;i--) if (deep[fa[x][i]]>=deep[y]) x=fa[x][i];
32     if (x==y) return x;
33     for (int i=16;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
34     return fa[x][0];
35 }
36 int main()
37 {
38     scanf("%d",&n);
39     for (int i=1,x;i<=n;i++) 
40     {
41         scanf("%d",&x);
42         while (x) insert(x,i),scanf("%d",&x),d[i]++;
43     }
44     for (int i=1;i<=n;i++) if (!d[i]) insert(n+1,i),d[i]++;
45     topusort();
46     for (int i=1;i<=n+1;i++)
47     {
48         int x=a[i],y=0;
49         for (int j=last[x];j;j=e[j].from) if (!y) y=e[j].to; else y=getlca(e[j].to,y);
50         fa[x][0]=y,deep[x]=deep[y]+1;
51         for (int j=1;j<=16;j++) fa[x][j]=fa[fa[x][j-1]][j-1];
52     }
53     for (int i=n+1;i>=1;i--) size[a[i]]++,size[fa[a[i]][0]]+=size[a[i]];
54     for (int i=1;i<=n;i++) printf("%d\n",size[i]-1);
55 }

 

转载于:https://www.cnblogs.com/Comfortable/p/11192853.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值