[日常训练]大灾难

Posted Aireen Ye

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[日常训练]大灾难相关的知识,希望对你有一定的参考价值。

Description

在一个生态圈中,食物链的维系是很重要的。食物链的断裂往往引起连锁反应,进而招致生态系统如同多米诺骨牌一样坍塌。

现在考虑一个简化模型。在一个生态系统中,有$N$种生物,它们分为两类:生产者与消费者。生产者通过这个系统之外的能量来生存,最常见的是植物的光合作用。而消费者需要“消费”,也就是以其他生物为食物才可以生存。为了简化问题,我们假设所有消费者是可以分层的,高一层的消费者可能的食物来源都来自它的严格下层。生产者可以视为最下层的成员。当一种生物灭绝之后,依赖于它的消费者会由于所有食物消失而灭绝。 而这些消费者的灭绝可能进一步引起它的上层成员灭绝。我们定义指标$C(x)$,用于表示当x从生态系统中消失之后,随它灭绝的生物数量。

给定一个食物链模型,对所有生物,计算它的灭绝指标$C(x)$。

Input

第一行一个整数$N,表示生物的数量。生物从$1$开始编号。

接下来$N$行,第$i$行表示第$i$种生物的食物列表。两种食物的编号以空格隔开,输入$0$表示本行结束。如果这一行只有一个$0$,这是一个生产者,不能认为它没有食物而天然灭绝。

Output

共$N$行,第$i$行是第$i$个生物的灭绝指标$C(i)$。

Sample Input

5
0
1 0
1 0
2 3 0
2 0

Sample Output

4 1 0 0 0

HINT

$N\;\leq\;65534$,输入保证合法(即可以分层)。输入文件大小不超过$1MB$。

Solution

显然,整张食物网是$DAG$.

利用$topo$序将图分层.

一个物种会灭亡当且仅当它的所有食物的公共祖先灭亡.

建树,每个物种的所有食物的公共祖先向每个物种连一条边,求子树大小.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 17
#define N 65535
#define M 300000
using namespace std;
struct graph{
    int nxt,to;
}e[N<<1],e1[M],e2[M];
struct lives{
    int n,dep;
}a[N];
int f[N][K],t[N],g[N],g1[N],g2[N],dep[N],siz[N],n,cnt,tot;
queue<int> q;
inline void adde1(int x,int y){
    e1[++cnt].nxt=g1[x];g1[x]=cnt;e1[cnt].to=y;
}
inline void adde2(int x,int y){
    e2[++tot].nxt=g2[x];g2[x]=tot;e2[tot].to=y;
}
inline void addedge(int x,int y){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline bool cmp(lives x,lives y){
    return x.dep<y.dep;
}
inline void toposort(){
    int u;
    for(int i=1;i<=n;++i)
        if(!t[i]) q.push(i);
    while(!q.empty()){
        u=q.front();q.pop();a[u].dep=++tot;
        for(int i=g1[u];i;i=e1[i].nxt)
            if(!(--t[e1[i].to]))
                q.push(e1[i].to);
    }
}
inline void dfs(int u){
    siz[u]=1;
    for(int i=g[u];i;i=e[i].nxt){
        dfs(e[i].to);siz[u]+=siz[e[i].to];
    }
}
inline int swim(int x,int h){
    for(int i=0;h;++i,h>>=1)
        if(h&1) x=f[x][i];
    return x;
}
inline int lca(int x,int y){
    int i,t;
    if(dep[x]<dep[y]){
        t=x;x=y;y=t;
    }
    x=swim(x,dep[x]-dep[y]);
    if(x==y) return x;
    while(true){
        for(i=0;f[x][i]!=f[y][i];++i);
        if(!i) return f[x][0];
        x=f[x][i-1];y=f[y][i-1];
    }
}
inline void Aireen(){
    scanf("%d",&n);
    for(int i=1,k;i<=n;++i){
        while(true){
            scanf("%d",&k);
            if(!k) break;
            adde1(k,i);++t[i];adde2(i,k);
        } 
    }
    tot=cnt=0;toposort();
    for(int i=1;i<=n;++i)
        a[i].n=i;
    sort(a+1,a+1+n,cmp);
    for(int q=1,i,j,l;q<=n;++q){
        i=a[q].n;j=g2[i];
        if(j){
            l=e2[j].to;
            for(j=e2[j].nxt;j;j=e2[j].nxt)
                l=lca(e2[j].to,l); 
            addedge(l,i);
            f[i][0]=l;
            for(int k=1;k<K;++k)
                f[i][k]=f[f[i][k-1]][k-1];
            dep[i]=dep[l]+1;
        } 
        else{
            addedge(0,i);
            for(int k=0;k<K;++k)
                f[i][k]=0;
            dep[i]=1; 
        }
    }
    dfs(0);
    for(int i=1;i<=n;++i)
        printf("%d\n",siz[i]-1);
}
int main(){
    freopen("catas.in","r",stdin);
    freopen("catas.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}

以上是关于[日常训练]大灾难的主要内容,如果未能解决你的问题,请参考以下文章

Tableau server日常维护 17处理Tableau server 灾难性恢复

译文:18个实用的JavaScript代码片段,助你快速处理日常编程任务

维数灾难与PCA主成分分析

解密体育背后AI黑科技:花样滑冰动作识别多模视频分类和精彩片段剪辑

PRML 1.3 模型选择 1.4 维度灾难

集训队日常训练20181110 DIV210 题解及AC代码