[ZJOI2012] 灾难
ZJOI2012 灾难
题目描述
阿米巴是小强的好朋友。
阿米巴和小强在草原上捉蚂蚱。小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难。
学过生物的阿米巴告诉小强,草原是一个极其稳定的生态系统。如果蚂蚱灭绝了,小鸟照样可以吃别的虫子,所以一个物种的灭绝并不一定会引发重大的灾难。
我们现在从专业一点的角度来看这个问题。我们用一种叫做食物网的有向图来描述生物之间的关系:
一个食物网有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。保证输入的食物网没有环。
题解
ZJOI2012灾难
样例就是题目描述中的图
我们发现一个生物u的灭绝,如果可能对另一种生物v造成影响,只可能是因为u的等级比v低,也就是说,u是v的食物,或者是u是v的食物的食物
那么到这里应该可以发现,top序是满足这种顺序的
那么,不用说,拓扑排序是肯定要的
知道了这种操作顺序后怎么办呢?博主蒟蒻机房的另一位同学说可以拆点,也就是说,如果一个生物同时吃多种生物,那么把这种生物拆成多个点,向每个被它吃的生物都连一条边
使用上面这种思想后,就是下面这张图
然后能把4号点害死的就是多个4号点的lca,自下而上统计答案就可以了
但最后发现好像不是很好实现?(总之最后弃了)
正解:舍弃一些信息,在拓扑排序中,如果由u拓展的一个点v的入度减为了0,并且它还没有lca,就直接把它赋值为u,代表此时v的入度只有1,u就是它的lca
如果他已经有了lca,代表它的入度不止1(因为如果只有1的话,那么之前它是没有被访问过的,lca[v]=-1,直接赋值即可),更行一遍lca[v]=LCA(u,lca[v]),然后把v直接连向lca[v]
也就说我们要动态维护lca?每次倍增复杂度会不会很高?直接跳!
其实我也不知道为什么不会T,甚至比某些正解还要快
跳完之后直接dfs统计答案即可
能证明复杂度请告知博主(真的很想知道)
Code
#include<bits/stdc++.h>
using namespace std;
const int N=70010;
void in(int &ans)
{
ans=0;int f=1;char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1;i=getchar();}
while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48),i=getchar();
ans*=f;
}
int n,cnt,cur,h=1,t;
int to[N<<1],nex[N<<1],head[N];
int Head[N],dp[N],dep[N],fa[N],dg[N],lca[N],q[N];
struct edge{
int to,next;
}e[N<<1];
inline void add(int a,int b)
{
to[++cnt]=b;
nex[cnt]=head[a];
head[a]=cnt;
}
inline void ad(int a,int b)
{
e[++cur].to=b;
e[cur].next=Head[a];
Head[a]=cur;
}
int LCA(int x,int y) {
if(!x || !y) return 0;
if(x==y) return x;
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=fa[x];
while(x!=y) x=fa[x],y=fa[y];
return x;
}
void dfs(int u) {
for(int i=Head[u];i;i=e[i].next) {
int to=e[i].to;
dfs(to); dp[u]+=dp[to]+1;
}
}
void topsort()
{
memset(lca,-1,sizeof(lca));
for(int i=1;i<=n;++i) if(!dg[i]) ad(0,i),q[++t]=i,dep[i]=1;
while(h<=t) {
int u=q[h++];
for(int i=head[u];i;i=nex[i]) {
if(lca[to[i]]==-1) lca[to[i]]=u;
else lca[to[i]]=LCA(u,lca[to[i]]);
if(!(--dg[to[i]])) {
fa[to[i]]=lca[to[i]]; dep[to[i]]=dep[lca[to[i]]]+1;
ad(lca[to[i]],to[i]); q[++t]=to[i];
}
}
}
}
int main()
{
in(n);
for(int i=1;i<=n;++i) {
int a;
while(1) {in(a); if(!a) break; add(a,i),dg[i]++;}
}
topsort(); dfs(0);
for(int i=1;i<=n;++i) printf("%d\n",dp[i]);
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
http://www.cnblogs.com/real-l/