2018-10-25杂题选讲
T5 灾难
原题
一个食物网有N个点,代表N种生物,如果生物x可以吃生物y,那么从y向x连一个有向边。
这个图没有环。
图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。
如果某个消费者的所有食物都灭绝了,它会跟着灭绝。
我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。
输入格式:
第一行点数N
接下来 N 行,每行描述了一个生物可以吃的其他生物的列表,格式为用空
格隔开的若干个数字,每个数字表示一种生物的标号,最后一个数字是 0 表示列
表的结束。
输出格式:
每行一个整数,表示每个生物的灾难值。
x指向y表示x吃y,那么若一个点p灭绝了意味着它没东西可吃,不能从p走到任何一个叶子。
那么对于一个命题,“已知若A灭绝则B灭绝,若B灭绝则C灭绝,那么若A灭绝则C灭绝”,显然成立。
这句话听起来就像“A能到B,B能到C,所以A能到C”,于是我们考虑建一个有向图,每条有向边表示若起点死了,终点也活不成,那么建出来的图一定是一棵树。
那么如果在这棵树上找答案就简单了,每个点的答案就是size[u]-1。
建树:若该节点只有一个父亲(这个父亲指反向的边,消费者指向生产者),则两点之间连一条边;若该节点有多个父亲,则将该节点与这些父亲的lca相连。从生产者开始慢慢建出这棵树(生产者为根,因为生产者死了其他人全挂),若有多个生产者则都连上n+1这个点。
拓扑排序:在把某个点加入树时,它的所有父亲必须已经在树中,相当于一层一层建这棵树,符合拓扑排序的思想,于是考虑先处理出建树的顺序。
#include<bits/stdc++.h>
using namespace std;
long long n,tmp1,first1[700070],nxt1[7000070],first2[700070],nxt2[700070],u1[7000070],v1[7000070],u2[7000070],v2[7000070],tot1=0,tot2=0;
long long topork[700070],degree[70070]={0},line[700070],l=1,r=0,tmp2,cnt=0;
long long maxn,tmp3,grand[700070][50]={0},lca,depth[700070]={0},size[700070];
//1为消费者指向生产者(吃它)
//2为生产者指向消费者(我死了它没东西吃)
void add1(long long from,long long to)
{
tot1++;
nxt1[tot1]=first1[from];
first1[from]=tot1;
u1[tot1]=from;
v1[tot1]=to;
return;
}
void add2(long long from,long long to)
{
tot2++;
nxt2[tot2]=first2[from];
first2[from]=tot2;
u2[tot2]=from;
v2[tot2]=to;
for (long long i=1;i<=maxn;i++) grand[to][i]=grand[grand[to][i-1]][i-1];
return;
}
void TOPO()
{
for (long long i=1;i<=n;i++) if (!degree[i]) line[++r]=i;
while (l<=r)
{
tmp2=line[l++];
topork[++cnt]=tmp2;
for (long long j=first1[tmp2];j!=-1;j=nxt1[j])
{
degree[v1[j]]--;
if (!degree[v1[j]]) line[++r]=v1[j];
}
}
}
long long LCA(long long a,long long b)
{
if (depth[a]<depth[b]) swap(a,b);
while (depth[a]!=depth[b])
{
int t=log2(depth[a]-depth[b]);
a=grand[a][t];
}
for (long long i=maxn;i>=0;i--)
{
if (grand[a][i]!=grand[b][i])
{
a=grand[a][i];
b=grand[b][i];
}
}
if (a!=b) a=grand[a][0];
return a;
}
void build()
{
grand[n+1][0]=n+1;
for (long long i=n;i>=1;i--)
{
tmp3=topork[i];
if (first1[tmp3]==-1)
{
grand[tmp3][0]=n+1;
depth[tmp3]=1;
add2(n+1,tmp3);
continue;
}
lca=v1[first1[tmp3]];
for (long long j=first1[tmp3];j!=-1;j=nxt1[j]) lca=LCA(lca,v1[j]);
grand[tmp3][0]=lca;
depth[tmp3]=depth[lca]+1;
add2(lca,tmp3);
}
return;
}
void dfs(long long point)
{
size[point]=1;
for (long long j=first2[point];j!=-1;j=nxt2[j])
{
long long to=v2[j];
dfs(v2[j]);
size[point]+=size[v2[j]];
}
return;
}
int main()
{
memset(first1,-1,sizeof(first1));
memset(first2,-1,sizeof(first2));
scanf("%lld",&n);
maxn=log2(n);
for (long long i=1;i<=n;i++)
{
scanf("%lld",&tmp1);
while (tmp1)
{
add1(i,tmp1);
degree[tmp1]++;
scanf("%lld",&tmp1);
}
}
TOPO();
build();
dfs(n+1);
for (long long i=1;i<=n;i++) printf("%lld\n",size[i]-1);
return 0;
}