BZOJ2875 灾难

Description

n 个点 m条边的DAG,对于每个点,求它的重要度。重要度定义如下:

对于 vv这个点,将其(和其相连的所有边)删去。之后考察所有点,如果某个点初始时出度不为 0而当前出度为 0,则将这个点也删去。重复这个过程直到没有点被删去。 v 的重要度即为除它以外删掉的点的个数。


/**************************************************************

    Problem: 2815

    User: ictsing

    Language: C++

    Result: Accepted

    Time:1088 ms

    Memory:95232 kb

****************************************************************/

 

//2815: [ZJOI2012]灾难

/*

n个生物,每个生物可以吃一些生物

灾难值指的是他挂掉以后有几种动物会挂掉

(给一个有向无环图,问每个节点删掉之后会导致多少个点不可达。)

 

显然,可以dfs拓扑排序,然后暴力搞O(n^2)

更妙的方法==》灭绝树

对于一棵多叉树的任意一个结点,当它“灭绝”时,它所有的后代也会跟着“灭绝”。

一种生物会“灭绝”只有两种情况:一是它原本就“灭绝”,二是它所有的食物都“灭绝”。

对于二

假设有生物I。很显然,只有当生物I的所有食物的最近公共祖先(LCA)J“灭绝”时,

生物I的所有食物才会同时“灭绝”(显然这里就要用到倍增求LCA的算法),

然后,我们将生物I作为生物J的一个新的孩子节点,表示当J“灭绝”时,

I也会跟着“灭绝”(因为它所有的食物都“灭绝”了)。

根据题目需求,最后只要输出每种生物在“灭绝树”中的所有后代数量

(数量的计算可以在每次“灭绝树”加入新节点时进行一次DFS)就可以了。

*/

#include <iostream>

#include <cstdio>

#include <cstring>

#include <vector>

using namespace std;

const int mm = 5000000+5;

const int mt = 100000+5;//我也不知到应该开多大,尽量开

vector<int>food[mt];

int to[mm<<1],nxt[mm<<1],adj[mt];

int ecnt=0;

int pnt,lst[mt],vis[mt];

int pa[mt][25],dep[mt];

void topu(int x)

{

    vis[x]=1;

    for(int i=adj[x];i;i=nxt[i])

    {

        int v=to[i];

        if(!vis[v]) topu(v);

    }

    lst[pnt--]=x;

}

void adde(int u,int v)

{

    ecnt++,to[ecnt]=v,nxt[ecnt]=adj[u],adj[u]=ecnt;

}

int sz[mt];

void dfs(int u)

{

    sz[u]=1;

    for(int i=adj[u];i;i=nxt[i])

    {

        int v=to[i];

        dfs(v),sz[u]+=sz[v];

    }

}

int llccaa(int u,int v)

{

    if(dep[u]<dep[v]) swap(u,v);

    for(int k=20;k>=0;k--)

        if(((dep[u]-dep[v])>>k)&1)

            u=pa[u][k];

    if(u==v) return u;

    for(int k=20;k>=0;k--)

        if(pa[u][k]!=pa[v][k])

            u=pa[u][k],v=pa[v][k];

    return pa[u][0];

}

inline int read()

{

    int num=0,flag=1;

    char ch;

    do{

        ch=getchar();

        if(ch=='-') flag=-1;

    }while(ch<'0'||ch>'9');

    do{

        num=num*10+ch-'0';

        ch=getchar();

    }while(ch>='0'&&ch<='9');

    return num*flag;

}

int main()

{

    int n=read();

    for(int i=1;i<=n;i++)

    {

        int x=read();

        if(x)

            for(;x;x=read())

                food[i].push_back(x),adde(x,i);

        else food[i].push_back(n+1),adde(n+1,i);

    }

    pnt=++n;

    topu(n);    

    //for(int i=1;i<=n;i++)

        //cout<<lst[i]<<endl;

    memset(to,0,sizeof(to));

    memset(adj,0,sizeof(adj));

    memset(nxt,0,sizeof(nxt));

    ecnt=0;

    dep[n]=1;

    for(int i=2;i<=n;i++)

    {

        int lca=0,u=lst[i];

        for(int v=0;v<food[u].size();v++)//u和i打反我也很难过>^<

        {

            if(lca==0) lca=food[u][v];

            else lca=llccaa(lca,food[u][v]);

        }

        adde(lca,u);

        //cout<<lca<<" "<<u<<endl;

        dep[u]=dep[lca]+1;

        pa[u][0]=lca;

        for(int k=1;k<=20;k++)

            pa[u][k]=pa[pa[u][k-1]][k-1];

    }

    dfs(n);

    for(int i=1;i<n;i++)

        printf("%d\n",sz[i]-1);

    return 0;

}

 


评论

热度(5)