[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/

posted @ 2018-08-24 09:00  real_l  阅读(478)  评论(0编辑  收藏  举报