[ZJOI2012]灾难
题目:洛谷P2597、BZOJ2815(然而此处并没有题面)、codevs1210
题目大意:给你一个食物网,要你求每个生物的“毁灭值”(毁灭值为该生物灭绝后,其他跟着它灭绝的生物的总数)。
解题思路:拓扑排序+LCA。
先假设所有生产者都吃“太阳”,然后对它们进行拓扑排序。以拓扑序依次加点,每次将要加的点与所有父亲求LCA,此时LCA或LCA的任一父亲节点死亡,则该点灭绝。所以直接把该点变为LCA的子节点即可。此时每个节点的毁灭值为以它为根的子树的节点的个数。
不过我的代码在codevs上莫名的RE了,知道原因的可以告诉我。
C++ Code:
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int n;
vector<int>p[65536],c[65536],nc[65536];
queue<int>tp;
int has[65536],dep[65536],s[65536][18],ans[65536];
void topo(){
queue<int>q;
q.push(0);
while(!q.empty()){
int v=q.front();
q.pop();
tp.push(v);
for(int i=0;i<c[v].size();i++)
if(!--has[c[v][i]])q.push(c[v][i]);
}
}
int LCA(int x,int y){
int i,j;
if(x==-1)return y;
if(dep[x]<dep[y])swap(x,y);
for(i=0;(1<<i)<=dep[x];i++);i--;
for(j=i;j>-1;j--){
if(dep[x]-(1<<j)>=dep[y])x=s[x][j];
}
if(x==y)return x;
for(j=i;j>=0;j--)
if(s[x][j]!=s[y][j])
x=s[x][j],y=s[y][j];
return s[x][0];
}
void getans(int x){
for(int i=0;i<nc[x].size();i++){
getans(nc[x][i]);
ans[x]+=ans[nc[x][i]]+1;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
while(scanf("%d",&x),x){
p[i].push_back(x);
c[x].push_back(i);
has[i]++;
}
if(!p[i].size()){
p[i].push_back(0);
c[0].push_back(i);
has[i]=1;
}
}
topo();
while(!tp.empty()){
int u=tp.front();tp.pop();
int lca=-1;
for(int i=0;i<p[u].size();i++)lca=LCA(lca,p[u][i]);
nc[lca].push_back(u);
s[u][0]=lca;
dep[u]=dep[lca]+1;
for(int i=1;(1<<i)<=dep[u];i++)s[u][i]=s[s[u][i-1]][i-1];
}
getans(0);
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号