bzoj2815 灾难

https://www.cnblogs.com/Slrslr/p/9578471.html

构造一颗“灭绝树”。即对于灭绝树上的两个点x,y,如果x为y的祖先,则x的灭亡会直接导致y的灭亡也就是说X是Y的食物。下面进行构造:首先进行拓扑排序,然后按照排序的逆序构造,保证对于图中的任意x->{y},都能使x构造前,{y}已经完成构造。然后找到{y}在灭绝树上面的位置,显然{y}在灭绝树上的公共祖先的灭绝会导致{y}全部灭绝进而导致x灭绝。于是可以求它们的lca,那么lca就是x在灭绝树上面的祖先。最后跑一边dfs统计子树大小即可。实际上,这是求有向图必经点的一个特殊问题(无环),实际上可以直接用支配树解决.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define M 100010
using namespace std;
int n,num1,num2,num3;
int head1[M],head2[M],head3[M],in[M],topo[M],deep[M],ans[M],fa[M][20];
struct point{int to,next;}e1[M<<1],e2[M<<1],e3[M<<1];
void add1(int from,int to) 
//from是食主人,TO是被吃的 
{
e1[++num1].next=head1[from];
e1[num1].to=to;
head1[from]=num1;
}

void add2(int from,int to) 
{
e2[++num2].next=head2[from];
e2[num2].to=to;
head2[from]=num2;
}

void add3(int from,int to) 
//from是食主人,TO是被吃的
{
e3[++num3].next=head3[from];
e3[num3].to=to;
head3[from]=num3;
}
void topsort()
{
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(!in[i]) //将没有前置食物的动物加入 
            q.push(i);
    int tot=0;
    while(!q.empty())
    {
        int now=q.front(); 
		q.pop();
        topo[++tot]=now;
        for(int i=head2[now];i;i=e2[i].next)
        {
            int to=e2[i].to;
            in[to]--;
            if(!in[to]) q.push(to);
        }
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])swap(x,y);
    for(int i=18;i>=0;i--)
        if(deep[fa[x][i]]>=deep[y])
            x=fa[x][i];
    if(x==y)return y;
    for(int i=18;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0]; 
}
void dfs(int x)
{
    for(int i=head3[x];i;i=e3[i].next)
    {
        int to=e3[i].to;
        dfs(to);
        ans[x]+=ans[to];
    }
    ans[x]++;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        while(1)
        {
            int x; 
			scanf("%d",&x);
			//x是第I种动物的食物 
            if(!x) break;
            add1(i,x); 
			add2(x,i); 
			in[i]++;//I这种动物的食物有几种 
        }
    }
    topsort();
    for(int i=1;i<=n;i++)
    {
        int x=e1[head1[topo[i]]].to;
        for(int j=head1[topo[i]];j;j=e1[j].next) 
		    x=lca(x,e1[j].to);//取出所有食物的等级最高的 
        add3(x,topo[i]);//建立一条边从X到它的食材等级最高的那个 
        deep[topo[i]]=deep[x]+1;//注意DEEP值最大,说明越是自然界食物链的上层 
        fa[topo[i]][0]=x;
        for(int j=1;j<=18;j++) //由于在不断建树,所以不断建立LCA的准备工作 
		    fa[topo[i]][j]=fa[fa[topo[i]][j-1]][j-1];
    }
    dfs(0);//统计每个点的子树大小 
    for(int i=1;i<=n;i++) 
	    printf("%d\n",ans[i]-1);
    return 0;
}

  

 

posted @ 2020-04-13 20:14  我微笑不代表我快乐  阅读(117)  评论(0编辑  收藏  举报