洛谷 P2597 [ZJOI2012]灾难 解题报告

P2597 [ZJOI2012]灾难

题目描述

阿米巴是小强的好朋友。

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

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

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

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

这个图没有环。

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

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

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

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

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

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

输入输出格式

输入格式:

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

号到 N。

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

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

表的结束。

输出格式:

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

数据规模

对50%的数据,N ≤ 10000。

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

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


吐槽:65534什么的,难道不是诱导去写bitset吗??

从来没见过这种思路的题,感觉十分神

规定 食物向捕食者连边组成的图称为正图,捕食者向食物连边的图称为反图

在正图中,如果刚好组成了一颗树,那么每个点的答案其实就是子树大小-1

(这个点死掉了,它下面的捕食者也凉凉)

现在如果加了一个点,使这个正图不构成一颗树了,怎么办呢?

我们看它在反图中的直接连边,指向它的食物,如果它的食物全在正图已经构成的一颗树上了,那么想让它的食物死光且只能死一个,那么死的只能是所以食物的LCA了

于是我们新弄一个图,把它连上它的食物的lca,这个图是一颗树

因为要保证所以食物已经在树上,所以要对正图做拓扑排序

LCA可以动态ST表一下或者LCT


Code:

#include <cstdio>
#include <vector>
using namespace std;
const int N=65536;
vector <int> to1[N],to2[N];
int to[N<<4],Next[N<<4],head[N],cnt,n;
void add(int u,int v)
{
    to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int q[N],l=1,r=0;
int f[N][18],dep[N],in[N];
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=16;i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=16;i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int dfn[N],low[N],dfs_clock;
void dfs(int now)
{
    dfn[now]=++dfs_clock;
    for(int i=head[now];i;i=Next[i])
        dfs(to[i]);
    low[now]=dfs_clock;
}
int main()
{
    scanf("%d",&n);
    for(int eat,i=1;i<=n;i++)
    {
        scanf("%d",&eat);
        while(eat)
        {
            to1[i].push_back(eat);//反图连边用
            to2[eat].push_back(i);//正图拓扑用
            ++in[i];
            scanf("%d",&eat);
        }
    }
    for(int i=1;i<=n;i++)
        if(!in[i])
        {
            add(0,i);
            f[i][0]=0;
            q[++r]=i;
            dep[i]=1;
        }
    while(l<=r)
    {
        int now=q[l++];
        for(int i=0;i<to2[now].size();i++)
        {
            int v=to2[now][i];
            --in[v];
            if(!in[v])
            {
                q[++r]=v;
                int lca=to1[v][0];
                for(int j=1;j<to1[v].size();j++)
                    lca=LCA(lca,to1[v][j]);
                add(lca,v);
                f[v][0]=lca;
                dep[v]=dep[lca]+1;
                for(int j=1;j<=16;++j)
                    f[v][j]=f[f[v][j-1]][j-1];
            }
        }
    }
    dfs(0);
    for(int i=1;i<=n;i++)
        printf("%d\n",low[i]-dfn[i]);
}

2018.9.2

posted @ 2018-09-02 07:53  露迭月  阅读(185)  评论(0编辑  收藏  举报