[ZJOI2012]灾难(拓扑排序)

烂漫一生 提交于 2020-03-02 08:56:58

[ZJOI2012]灾难(luogu)

Solution

  • 由于没有环,可以看出食物网的分级结构,令生产者-初级消费者-...等级越来越高
  • 对于每种生物,能使它灭绝的等级最低的生物至多有一种,为能使 每种吃它的生物 灭绝的等级最低的生物
  • 可以建一棵灭绝树,树上每个节点的父亲即为能使它灭绝的等级最低的生物
  • 求父亲即为求每种吃它的生物在灭绝树上对应的点的 LCA
  • 可以看出求某个点的父亲时每种吃它的生物的父亲应已求出
  • 在原食物网上跑一个拓扑,由于每种吃它的生物的拓扑排序必然在它前面,可以按这个顺序建树
  • 为将森林化成树,将原食物网上所有入度为 0 的点的父亲指向 0

Code

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N=5e5+10;
int ru[N],top[N];
int n,m,root,dep[N],fa[N][20],num,sum,si[N],d[N];
vector <int> l1[N],l2[N];
queue <int> q;
int lca(int x,int y)
{
    if(dep[x]>dep[y]) swap(x,y);
    for(int i=19;i>=0;i--)
        if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];
    if(y==x) return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int dfs(int u)
{
    int size=l2[u].size();
    for(int i=0;i<size;i++)
        si[u]+=dfs(l2[u][i]);
    return si[u];
}
int main()
{
    scanf("%d",&n);
    for(int x=1,y;x<=n;x++)
    {
        si[x]=1;
        while(scanf("%d",&y) && y)
            l1[y].push_back(x),ru[x]++;
        if(ru[x]==0) q.push(x);
        else top[x]=-1;
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        l2[top[u]].push_back(u);
        dep[u]=dep[top[u]]+1,fa[u][0]=top[u];
        for(int i=1;i<20;i++)
            fa[u][i]=fa[fa[u][i-1]][i-1];
        int size=l1[u].size();
        for(int i=0;i<size;i++)
        {
            int v=l1[u][i];
            if(top[v]==-1) top[v]=u;
            else top[v]=lca(top[v],u);
            if(--ru[v]==0) q.push(v);
        }
    }
    dfs(0);
    for(int i=1;i<=n;i++)
        printf("%d\n",si[i]-1);
    return 0;
}

 

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!