传送门:bzoj2815
题解
虽然此题tag是dp…
先拓扑排序一波,然后建立一颗名为“灭绝树”的树,对于该树,满足如下定义:
对于树中的每个节点,若该节点的生物灭绝,那么以它为跟的子树内的所有节点的生物都会跟着灭绝。
按拓扑序倒序加入“灭绝树”(仔细读一下题,你就会发现拓扑序大的才是王者),建的时候连在所有它的食物点的lca下就好了(lca灭绝代表,所有它可以吃的都灭绝了)。
代码
#include<bits/stdc++.h>
#define gc getchar()
using namespace std;
const int N=1e5+10;
int in[N],head[N],to[N],nxt[N],tot,sz[N];
int n,cnt,bin[22],tp[N],f[N][21],d[N];
vector<int>g[N];
vector<int>fir;
inline int rd()
{
char ch=gc;int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=gc;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=gc;}
return x*f;
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;in[v]++;}
inline void dfs(int x)
{
for(int i=g[x].size()-1;i>=0;i--){
if(g[x][i]==f[x][0]) continue;
f[g[x][i]][0]=x;
dfs(g[x][i]);
sz[x]+=(sz[g[x][i]]+1);
}
}
inline int LCA(int x,int y)
{
if(x==-1) return y;
if(d[x]<d[y]) {int t=x;x=y;y=t;}
int t=d[x]-d[y];
for(int i=0;bin[i]<=t;i++){
if(bin[i]&t) x=f[x][i];
}
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
}
if(x!=y) return f[x][0];
else return x;
}
int main(){
bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
n=rd();
for(int x,i=1;i<=n;i++){x=rd();while(x!=0){lk(i,x);x=rd();}}
for(int i=1;i<=n;i++) if(in[i]==0) fir.push_back(i);
while(!fir.empty()){
int e=fir.back();
fir.pop_back();
tp[++cnt]=e;
for(int j=head[e];j;j=nxt[j]){
in[to[j]]--;
if(in[to[j]]==0) fir.push_back(to[j]);
}
}
for(int i=cnt;i;i--){
int e=tp[i],lca=-1;
for(int j=head[e];j;j=nxt[j]) lca=LCA(lca,to[j]);
if(lca==-1) lca=0;
g[lca].push_back(e);
d[e]=d[lca]+1;f[e][0]=lca;
for(int j=1;bin[j]<=d[e];j++){
f[e][j]=f[f[e][j-1]][j-1];
}
}
dfs(0);
for(int i=1;i<=n;i++) printf("%d\n",sz[i]);
return 0;
}