[ ZJOI 2012 ] 灾难

\(\\\)

Description


给出一个食物网,每个生物指向的生物都是它可以捕食的对象,保证是图是DAG。

如果一个捕食者的所有捕食对象都灭绝了,那么它们也会灭绝。

求每一个动物灭绝之后,有多少个动物会随之灭绝。

  • \(n\le 65534\)

Solution


考察建图思维的好题。

如果我们按照 捕食者\(\to\)捕食对象 的关系连边,那么我们得到的是一张捕食关系的流向图,无法判断反向的影响。

所以要按照 捕食对象\(\to\)捕食者 的关系连边,得到的是一张能量供应的流向图。

那么形象的说,每个点都把能量流向出边指向的点,如果一个点消失了,从他这里流出去的所有能量全都消失。

如果这个时候某些点没有能量流入,那么这些点就是需要统计到消失点的答案里。

但是只有这些吗?并不是。因为这些受影响消失的点可能继续导致一些点消失,后面的答案我们没有统计。

我们设 \(die_x\) 表示节点 x 如果消失,有连边的点直接消失的点的个数。

那么我们一个点要累加的,就是所有直接指向的消失的点的个数+这些点的 \(die\) 值。


考虑如何求出 \(die\) 值。我们发现这就是能量供应关系的图的拓扑序。

我们新建一棵树形结构,父子关系表示,如果父节点消失,子节点必然消失。

首先将这张能量供应图中所有入度为 \(0\) 的点连向虚拟根节点,代表他们不会因为别的生物灭亡而灭亡。

然后考虑拓扑到一个点时,它应该作为哪个节点的子节点。显然是它的所有食物在这棵树上的 \(LCA\)

因为拓扑,所以他的所有食物必然已经在这个树上,我们两两合并求 \(LCA\) 即可。

因为要找所有在能量供给图上指向当前点的点,所以我们还要把捕食的图建出来,便于找食物有哪些。

因为求 \(LCA\) 多次,需要 \(log\) 的复杂度,因为树的形态是变化的,所以链剖和离线算法显然都不行。

考虑倍增。查找两两合并是没有问题了,但是新插入节点怎么办?

注意到倍增数组的构造只需要知道父节点,所以在把拓扑到的点插入树的同时构造一下倍增数组就好了。

因为这个树的定义,每一个点的答案显然是子树大小 \(-1\)

Code


一直 \(90\) 不知道为什么,后来发现........

\(LCA\) 的时候根节点设成了 \(n+1\) ,但是如果倍增的时候深的跳超了,但是另一个是根节点,根据判断条件并没有跳超深度,它就会跳到 0 号节点然后 GG....

\(n+1\) 号节点设个深度,或者根节点换成 \(0\) 号节点就好啦

#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 70000
#define R register
#define gc getchar
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,m,tot,hd[N],deg[N],tot2,hd2[N];

int t,tot1,d[N],hd1[N],f[N][20],ans[N];

struct edge{int to,nxt;}e[N<<3],e1[N<<1],e2[N<<3];

inline void add(int u,int v){
  e[++tot].to=v; e[tot].nxt=hd[u]; hd[u]=tot;
}

inline void add1(int u,int v){
  e1[++tot1].to=v; e1[tot1].nxt=hd1[u]; hd1[u]=tot1;
}

inline void add2(int u,int v){
  e2[++tot2].to=v; e2[tot2].nxt=hd2[u]; hd2[u]=tot2;
}

inline void getfa(int u,int fa){
  f[u][0]=fa; d[u]=d[fa]+1;
  for(R int i=1;i<=t;++i) f[u][i]=f[f[u][i-1]][i-1];
}

inline int lca(int u,int v){
  if(d[u]>d[v]) u^=v^=u^=v;
  for(R int i=t;~i;--i) if(d[f[v][i]]>=d[u]) v=f[v][i];
  if(u==v) return u;
  for(R int i=t;~i;--i)
    if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
  return f[u][0];
}

inline void work(int u){
  int res=e2[hd2[u]].to;
  for(R int i=e2[hd2[u]].nxt;i;i=e2[i].nxt) res=lca(res,e2[i].to);
  getfa(u,res); add1(u,res); add1(res,u);
}

void dfs(int u,int fa){
  for(R int i=hd1[u],v;i;i=e1[i].nxt)
    if((v=e1[i].to)!=fa){
      dfs(v,u); ans[u]+=ans[v];
    }
}

queue<int> q;

int main(){
  t=log2(n=rd())+1;
  for(R int i=1,x;i<=n;++i){
    x=rd();
    while(x){
      ++deg[i];add(x,i);
      add2(i,x);x=rd();
    }
    ans[i]=1;
  }
  for(R int i=1;i<=n;++i)
    if(!deg[i]){
      q.push(i); getfa(i,0);
      add1(i,0); add1(0,i);
    }
  while(!q.empty()){
    int u=q.front(); q.pop();
    for(R int i=hd[u],v;i;i=e[i].nxt)
      if((--deg[v=e[i].to])==0) q.push(v),work(v);
  }
  dfs(0,-1);
  for(R int i=1;i<=n;++i) printf("%d\n",ans[i]-1);
  return 0;
}
posted @ 2018-11-12 21:14  SGCollin  阅读(127)  评论(0编辑  收藏  举报