您的位置:首页 > 其它

[ZJOI2012] 灾难

2018-03-29 09:09 162 查看

题目描述:

雾.

题目分析:

本题出题人题解:http://fanhq666.blog.163.com/blog/static/8194342620124274154996/

有这样一个事实:

生物之间的灭绝的结构形成了一个树,树上的一个节点的灭绝会且仅会导致以它为根的子树的灭绝。我们管这个树叫“灭绝树”。

对于生产者,我们给它添加一个假想的食物:太阳。

这样,“灭绝树”就形成了一个以太阳为根的树。

下面说明如何通过增量法把灭绝树建出来,同时也是对灭绝树的存在性的证明。

首先,把食物网按从猎物到捕食者的顺序拓扑排序。

之后,依次考虑每个生物i.我们已经构建好了排序在i之前的生物组成的“灭绝树”了。

假设i的食物有x[0]~x[k](x[0]~x[k]在拓扑排序中比i靠前)。

很显然,只有x[0]~x[k]在树上的公共祖先的灭绝会导致i灭绝,否则i一定可以找到能让它活下来的食物。

                 

            ...  

         /       

        /        

      LCA        

      /|\        

    _/ ||        

   /   | \       

  O    |  \      

 / \    \  \     

x   x    x  x    

                 

       i         

                 

                 

于是,我们可以把i挂在x[0]~x[k]的最近公共祖先下面。

处理完所有的生物,我们得到的树就是整个图的灭绝树了。

一旦得到灭绝树,每个生物的灾难值就可以通过以它为根的子树的大小减1来计算.

题目链接:

Luogu 2597

Ac 代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define cnt Cnt[f]
const int maxm=65540;
int siz[maxm],son[maxm],top[maxm],fax[maxm],deep[maxm],f[maxm][17];
int head[2][maxm],to[2][maxm<<2],net[2][maxm<<2],Cnt[2];
int r[maxm],topdl[maxm];
std::queue<int> dl;
int ans[maxm],n;
inline void addedge(int x,int y,bool f)
{
to[f][++cnt]=y;
net[f][cnt]=head[f][x],head[f][x]=cnt;
}
inline int LCA(int x,int y)
{
if(deep[x]<deep[y]) std::swap(x,y);
for(int i=16;i>=0;i--)
if(f[x][i]&&deep[f[x][i]]>deep[y]) x=f[x][i];
if(x==y) return y;
for(int i=16;i>=0;i--)
if(f[x][i]&&f[y][i]&&f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return fax[x];
}
inline void addst(int x)
{
f[x][0]=fax[x];
for(int i=1;i<=16;i++)
f[x][i]=f[f[x][i-1]][i-1];
}
inline void topsort()
{
int now=0;
for(int i=1;i<=n;i++)
if(!r[i]) dl.push(i);
while(!dl.empty())
{
int x=dl.front();
dl.pop();
topdl[++now]=x;
for(int i=head[0][x];i;i=net[0][i])
{
int tmp=to[0][i];
r[tmp]--;
if(!r[tmp]) dl.push(tmp);
}
}
}
inline void makemap()
{
topsort();
deep[n+1]=1,fax[n+1]=n+1;
for(int i=n;i>=1;i--)
{
int x=topdl[i];
if(!head[0][x])
{
fax[x]=n+1,deep[x]=2;
addedge(n+1,x,1);
f[x][0]=n+1;
continue;
}
int lca=to[0][head[0][x]];
for(int j=net[0][head[0][x]];j;j=net[0][j])
{
int tmp=to[0][j];
lca=LCA(lca,tmp);
}
fax[x]=lca,deep[x]=deep[lca]+1;
addedge(lca,x,1);
addst(x);
}

}
void dfs(int now)
{
ans[now]=1;
for(int i=head[1][now];i;i=net[1][i])
{
int tmp=to[1][i];
dfs(tmp);
ans[now]+=ans[tmp];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
while(1)
{
scanf("%d",&x);
if(!x) break;
addedge(i,x,0);
r[x]++;
}
}
makemap();
dfs(n+1);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]-1);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: