P2597 [ZJOI2012]灾难 拓扑排序

这个题有点意思,正常写法肯定会T,然后需要优化。先用拓扑排序重构一遍树,然后进行一个非常神奇的操作:把每个点放在他的食物的lca上,然后计算的时候直接dfs全加上就行了。为什么呢,因为假如你的食物的lca死了,你就很自然的死了。这个题还要加一个超级源点,一开始我没加只拿了20分。但是不知道为什么,后来想到有可能一个兄弟能吃两个生产者,在查询lca的时候会跪。。。剩下就没啥了,顺便练一下倍增求lca。

题干:

题目描述

阿米巴是小强的好朋友。

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

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

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

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

这个图没有环。

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

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

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

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

如

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

给定一个食物网,你要求出每个生物的灾难值。
输入输出格式
输入格式:

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

号到 N。

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

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

表的结束。

输出格式:

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

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(register int i = a;i <= n;++i)
#define lv(i,a,n) for(register int i = a;i >= n;--i)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 1e5 + 5;
struct node
{
    int l,r,nxt;
}a[N],b[N],c[N];
int len = 0,len1 = 0,len2 = 0,n;
int in[N],rk[N],num = 0;
int lst[N],lst1[N],lst2[N];
int f[N][35],ans[N],h[N];
void add(int x,int y)
{
    a[++len].l = x;
    a[len].r = y;
    a[len].nxt = lst[x];
    lst[x] = len;
}
void add1(int x,int y)
{
    b[++len1].l = x;
    b[len1].r = y;
    b[len1].nxt = lst1[x];
    lst1[x] = len1;
}
void add2(int x,int y)
{
    c[++len2].l = x;
    c[len2].r = y;
    c[len2].nxt = lst2[x];
    lst2[x] = len2;
}
void topsort()
{
    queue <int> q;
    duke(i,1,n)
    {
        if(!in[i])
        q.push(i);
    }
    int num = 0;
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        rk[++num] = now;
        for(int k = lst[now];k;k = a[k].nxt)
        {
            int y = a[k].r;
            in[y]--;
            if(!in[y])
            q.push(y);
        }
    }
}
int lca(int x,int y)
{
    if(h[x] < h[y])
    {
        swap(x,y);
    }
    int k = h[x] - h[y];
    for(int i = 0;i <= 30;i++)
    {
        if((k >> i) & 1)
        x = f[x][i];
    }
    if(x == y) return x;
    for(int i = 30;i >= 0;i--)
    {
        if(f[x][i] != f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
void dfs(int now)
{
    for(int k = lst2[now];k;k = c[k].nxt)
    {
        int y = c[k].r;
        dfs(y);
        ans[now] += ans[y];
    }
    ans[now]++;
}
int main()
{
    read(n);
    duke(i,1,n)
    {
        int x;
        read(x);
        while(x)
        {
            in[i]++;
            add1(i,x);
            add(x,i);
            read(x);
        }
    }
    topsort();
    duke(i,1,n)
    {
        int x = b[lst1[rk[i]]].r;
        for(int k = lst1[rk[i]];k;k = b[k].nxt)
        {
            int y = b[k].r;
            x = lca(x,y);
        }
        add2(x,rk[i]);
        h[rk[i]] = h[x] + 1;
        f[rk[i]][0] = x;
        for(int j = 1;j <= 30;j++)
        f[rk[i]][j] = f[f[rk[i]][j - 1]][j - 1]; 
    }
    dfs(0);
    duke(i,1,n)
    {
        printf("%d\n",ans[i] - 1);
    }
    return 0;
}

 

posted @ 2019-02-22 23:15  DukeLv  阅读(160)  评论(0编辑  收藏  举报