P4017最大食物链计数[拓扑法]

题目背景

一道经典的DP拓扑题

你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。

题目描述

给你一个食物网,你要求出这个食物网中最大食物链的数量。
(这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者.)
Delia 非常急,所以你只有1秒的时间。
由于这个结果可能过大,你只需要输出总数模上80112002的结果.

输入格式

第一行,两个正整数 n、m,表示生物种类n和吃与被吃的关系数 m.
接下来m行,每行两个正整数,表示被吃的生物A和吃A的生物B.

输出格式

一行一个整数,为最大食物链数量模上80112002的结果。

输入输出样例
输入#1
5 7
1 2
1 3
2 3
3 5
2 5
4 5
3 4
输出#1
5

说明/提示

各测试点满足以下约定:
P4017最大食物链计数[拓扑法]_第1张图片
思路方法

(最近本来在练习DP基础,本想用DP来解决这道题,但想一想似乎用拓扑排序做更优易理解(其实我根本不会DP ),所以还是练一练最近新学的拓扑吧.)
对于每一条食物链,如果我们用爆搜的话时间复杂度肯定连第三个点都过不了(大概是 O ( V E 2 ) {O(VE^2)} O(VE2)级别的吧(本人复杂度计算很菜 )).
用拓扑的话,先用邻接表存图,然后统计入读为0的点并将其压入队中,用数组 d [ i ] {d[i]} d[i]来存储第 i {i} i个节点的贡献值,将初状态入度为0的节点的贡献值赋为1,再遍历所有入度为0的点的连边的节点,将该节点的入度-1并将贡献值传到该点,一直这样重复直到所有节点入度为0,然后统计所有出度为0的点的贡献值的总和便是食物链的总数.时间复杂度 O ( V + E ) {O(V+E)} O(V+E).

代码

#include
#define N 500005
#define in read()
using namespace std;

queue<int>q;//用普通队列就行
int n,m,tot,p=80112002;
int zhi[N],rd[N],cd[N],fi[N],nxt[N],to[N];
long long d[N],ans=0;//最好开long long 不然可能会WA

inline int in{
	int i=0;char ch;
	while(!isdigit(ch)){ch=getchar();}
	while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch^48);ch=getchar();}
	return i;
}//快读加速

inline void add(int u,int v)//邻接表
{
	nxt[++tot]=fi[u];
	fi[u]=tot;
	to[tot]=v;
}

inline void tppx()//拓扑排序
{
	for(int i=1;i<=n;i++)
	if(!rd[i])
	{
		q.push(i);
		d[i]=1;
	}
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=fi[x];i;i=nxt[i])
		{
			int y=to[i];
			rd[y]--,d[y]=(d[y]+d[x]%p)%p;//不要忘记取模
			if(!rd[y])
			q.push(y);	
		}
		if(!cd[x])ans+=d[x]%p;//统计答案
	}
	return;
}

int main()
{
	int x,y;
	n=in,m=in;
	for(int i=1;i<=m;i++)
	{
		x=in,y=in;
		rd[x]++,cd[y]++;//统计出入度.
		add(y,x);//方向无所谓
	}
	tppx();
	printf("%d\n",ans%p);
	return 0;
}

你可能感兴趣的:(拓扑排序,———图论———)