题目描述
如图所示为某生态系统的食物网示意图,据图回答第1小题【满满的都是生物的气息emmmmm】
现在给你n个物种和m条能量流动关系,求其中的食物链条数。
物种的名称为从1到n编号
M条能量流动关系形如
a1 b1
a2 b2
a3 b3
……
am-1 bm-1
am bm
其中ai bi表示能量从物种ai流向物种bi,注意单独的一种孤立生物不算一条食物链
输入
第一行两个整数n和m,接下来m行每行两个整数ai bi描述m条能量流动关系。
(数据保证输入数据符号生物学特点,且不会有重复的能量流动关系出现)
1<=N<=100000 0<=m<=200000
题目保证答案不会爆 int
输出
一个整数即食物网中的食物链条数
样例输入
10 16
1 2
1 4
1 10
2 3
2 5
4 3
4 5
4 8
6 5
7 6
7 9
8 5
9 8
10 6
10 7
10 9
样例输出
9
一句话的切题
在一个给定的DAG图中,求任意一条从入度为0的点到出度为0的点的方案数,其中不包括入度和出度都为0的点
思路:
一上来就看成了和上一道并查集的食物链一样的题,恍惚了一下才发现这是个什么东西。餐后小甜点的训练中A题拓扑的题目就是这一道,用裸的迷迷的拓扑排序竟然还过了三个点(姿势好姿势好哈哈哈尬)
事实上没看出来应该用拓扑套上什么,各种跪着哭,最后听他们说是要dp,但是超级没有觉得DP在哪里。。。
首先入度和出度都为0的点要特判,然后设f[i]表示从某个入度为0的点到点i的方案数,然后按照类似拓扑排序的方法,由入度为0的点更新其它点,在此过程中进行累加。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define ri register int
typedef long long LL;
using namespace std;
const int sz = 100010;
inline void read(int &x){
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
if(f) x*=-1;
}
inline void we(int x){
if(x<0) putchar('-'),x*=-1;
if(x/10) we(x/10);
putchar(x%10+'0');
}
int n,m,to[sz<<1],fir[sz],nxt[sz<<1],tot;
int cd[sz],rd[sz],ans,f[sz];
inline void add(int f,int t){
to[++tot]=t;
nxt[tot]=fir[f];
fir[f]=tot;
}
queue<int>q;
int main()
{
int x,y;
read(n),read(m);
for(ri i=1;i<=m;++i)
{
read(x),read(y);
add(x,y);
cd[x]++,rd[y]++;
}
for(ri i=1;i<=n;++i)
{
if(!rd[i])
{
if(cd[i]) f[i]=1;
q.push(i);
}
}
while(!q.empty())
{
x = q.front();//这个x的定义问题有可能会造成3组超时...
q.pop();
for(ri i=fir[x];i;i=nxt[i])
{
f[to[i]]+=f[x],rd[to[i]]--;
if(!rd[to[i]])
q.push(to[i]);
}
}
for(ri i=1;i<=n;++i)
if(!cd[i])
ans+=f[i];
we(ans);
return 0;
}