[2018.12.28]BZOJ2815 [ZJOI2012]灾难
首先,我们需要把图上的边像样例的图片一样反着建。
然后我们跑一遍拓扑排序(注意这时候可能有多个入度为0的点),记录每个点的层数(即它的所有父亲的层数最大值+1)。
然后我们将所有点按层数升序排序,依次处理。
对于每个点,能够使它灭绝的,深度最大的点是它所有父亲的\(LCA\),记为\(f_{i,0}\)。
当然必然会有某一些点的所有父亲没有\(LCA\),或者它干脆没有父亲(①)。
这里就是要按层数处理节点的原因,可以确保处理一个点时,它所有的父亲都被处理过了。
显然\(f_{f_{i,0},0},f_{f_{f_{i,0},0},0},...\)也能使\(i\)灭绝。
由于\(f_{i,0}\)是唯一的,所以如果我们建一个新的图,其中所有\(f_{i,0}\)向\(i\)连边,就得到了一个森林。
森林中所有树的根都满足①,而且所有满足①的点必然是树根。
而一个点的灾难值就是它的子树中点的数量。
code:
#include<bits/stdc++.h>
using namespace std;
int n,t,in[65551],sq[65551],sz,f[65551][20],ans[65551],dep[65551],tg;
vector<int>e[65551];
vector<int>fs[65551];
vector<int>ze[65551];
queue<int>q;
void Tpst(){
for(int i=1;i<=n;i++)if(!in[i])q.push(i);
while(!q.empty()){
sq[++sz]=t=q.front();
q.pop();
for(int i=0;i<e[t].size();i++)if(!(--in[e[t][i]]))q.push(e[t][i]);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=16;i>=0;i--)if(dep[x]-(1<<i)>=dep[y])x=f[x][i];
for(int i=16;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
if(x==y)return x;
return f[x][0];
}
void dfs(int x){
ans[x]=1;
for(int i=0;i<ze[x].size();i++)dfs(ze[x][i]),ans[x]+=ans[ze[x][i]];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
while(scanf("%d",&t)){
if(!t)break;
e[t].push_back(i);
fs[i].push_back(t);
in[i]++;
}
Tpst();
for(int i=1;i<=sz;i++){
t=-1;
for(int j=0;j<fs[sq[i]].size();j++){
t=(t==-1?fs[sq[i]][j]:LCA(t,fs[sq[i]][j]));
}
if(t!=-1&&t!=0)f[sq[i]][0]=t;
if(t!=-1)dep[sq[i]]=dep[t]+1;
for(int j=1;j<=16&&f[sq[i]][j-1];j++)f[sq[i]][j]=f[f[sq[i]][j-1]][j-1];
if(f[sq[i]][0])ze[f[sq[i]][0]].push_back(sq[i]);
else q.push(sq[i]);
}
while(!q.empty())tg=(q.front()==1),dfs(q.front()),q.pop();
for(int i=1;i<=n;i++)printf("%d\n",ans[i]-1);
return 0;
}