【HAOI2016】食物链

时间:2023-03-10 06:04:00
【HAOI2016】食物链

HA真是弱……

原题:

1.食物链
【问题描述】
如图所示为某生态系统的食物网示意图,据图回答第1小题.

【HAOI2016】食物链

1.数一数,在这个食物网中有几条食物链(  )
现在给你n个物种和m条能量流动关系,求其中的食物链条数。
物种的名称为从1到n编号
M条能量流动关系形如
a1 b1
a2 b2
a3 b3
......
am-1 bm-1
am bm
其中 ai bi 表示能量从物种ai 流向物种bi

1<=N<=100000  0<=m<=200000

数据保证输入数据符合生物学特点,且不会有重复的能量流动关系出现

首先因为数据保证输入数据符合生物学特点,所以不用考虑环了,不会有重复的能量流动关系出现所以也不用考虑重边

然后很容易想出来递归一下,每个节点都加上出边对应的点的方案数,出边对应点的方案数递归到下一层求

这样每个节点的方案数就是从这个节点到某个没有出边的点的方案数,把这个节点的方案数加到上一层,上一层的点所有出边对应的点的方案数加起来就是上一层点的方案数

这个应该很好想

为了防止重复计算加上记忆化搜索,每个点至多访问一次,复杂度是满足的

但是有一个问题,最坏10^5层递归会把栈炸掉,这个时候就要把递归转化成迭代

然后用拓扑排序,这次反向建边,然后反向拓扑,这个节点的出边对应的节点加上这个节点的方案数即可,效果和上面的递归是等效的

加一个汇t方便计数(网络流做傻系列(注意如果这样做的话边的数组就要开大点

代码:

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read(){int z=,mark=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')mark=-; ch=getchar();}
while(ch>=''&&ch<=''){z=(z<<)+(z<<)+ch-''; ch=getchar();}
return z*mark;
}
const int oo=;
struct ddd{int next,y;}e[]; int LINK[],ltop=,in_degree[];
inline void insert(int x,int y){e[++ltop].next=LINK[x];LINK[x]=ltop;e[ltop].y=y; in_degree[y]++;}
int n,m; int t;
int QUEUE[],head=;
int f[];
int main(){//freopen("ddd.in","r",stdin);
cin>>n>>m; t=n+;
int _left,_right;
while(m --> ){
_left=read(),_right=read();
insert(_right,_left);
}
for(int i=;i<=n;i++)if(!in_degree[i] && LINK[i]) QUEUE[++head]=i,f[i]=;
for(int i=;i<=n;i++)if(!LINK[i]) insert(i,t);
for(int k=;k<=head;k++)
for(int i=LINK[QUEUE[k]];i;i=e[i].next){
in_degree[e[i].y]--,f[e[i].y]+=f[QUEUE[k]];
if(!in_degree[e[i].y]) QUEUE[++head]=e[i].y;
}
cout<<f[t]<<endl;
return ;
}