【题解】食物链 By 5ab as a juruo
题目
题目来源:CCF HAOI2016;
题目描述
如图所示为某生态系统的食物网示意图,据图回答第 \(1\) 小题。
现在给你 \(n\) 个物种和 \(m\) 条能量流动关系,求其中的食物链条数。物种的名称为从 \(1\) 到 \(n\) 编号。
\(m\) 条能量流动关系形如:
其中 \(a_i\)、\(b_i\) 表示能量从物种 \(a_i\) 流向物种 \(b_i\),注意单独的一种孤立生物不算一条食物链。
输入格式
第一行两个整数 \(n\) 和 \(m\);
接下来 \(m\) 行每行两个整数 \(a_i\)、\(b_i\) 描述 \(m\) 条能量流动关系。
输出格式
一个整数即食物网中的食物链条数。
数据规模及约定
\(1\le n\le 10^5\),\(0\le m\le 2\times 10^5\)。
保证输入数据符合生物学特点,且不会有重复的能量流动关系出现。题目保证答案不会爆 int。
分析
题意是给你一张 DAG,求出满足起点入度为 \(0\),终点出度为 \(0\) 的路径的数量。
这显然是一个很简单的搜索,最简单的方法就是从每一个入度为 \(0\) 的点进行 DFS 并统计。但是这并不一定可以通过。
注意到这是一个 DAG,所以我们进行记忆化。记录 \(f_i\) 为第 \(i\) 个点作为起点,终点出度为 \(0\) 的路径数量。
那么,对于一个节点,\(f_i\) 的值就是所有其后继节点的 \(f_j\) 的和,如果有一个后继节点没有后继节点,那么就对原节点 \(+1\),创建一条新的路径。
相比来说,这个算法的优点在于每一个节点只处理一次,复杂度是 \(O(n+m)\),绰绰有余。
Code
一定要记住,一个节点并不能算作一条路径,在代码中要体现这一点。
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int max_n = 100000, max_m = 200000; // 常量
int hd[max_n], des[max_m], nxt[max_m], ind[max_n] = {}, e_cnt = 0; // 邻接表存图和入度统计
bool vis[max_n] = {}; // 是否处理过
long long dp[max_n] = {}, ans = 0; // 记忆化
inline int read() // 快读
{
int ch = getchar(), t = 1, n = 0;
while (isspace(ch)) { ch = getchar(); }
if (ch == '-') { t = -1, ch = getchar(); }
while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
return n * t;
}
void add_edge(int s, int t)
{
des[e_cnt] = t;
nxt[e_cnt] = hd[s];
hd[s] = e_cnt++;
}
void dfs(int id) // 搜索
{
vis[id] = true; // 标记
if (hd[id] == -1) // 出度为0
{
dp[id] = -666; // 标记
return;
}
for (int p = hd[id]; p != -1; p = nxt[p]) // 遍历后继节点
{
if (!vis[des[p]]) // 处理
dfs(des[p]);
if (dp[des[p]] == -666) // 有标记
dp[id]++; // 新建一条路径
else
dp[id] += dp[des[p]]; // 否则累加
}
}
int main()
{
memset(hd, -1, sizeof(hd)); // 初始化
int n = read(), m = read(), ta, tb;
for (int i = 0; i < m; i++) // 输入
{
ta = read(), tb = read();
add_edge(ta - 1, tb - 1); // 建图
ind[tb-1]++; // 统计入度
}
for (int i = 0; i < n; i++)
if (!ind[i]) // 入度为0
{
dfs(i); // 计算
if (dp[i] != -666) // 不是单独节点
ans += dp[i]; // 统计
}
printf("%lld\n", ans); // 精准结束
return 0;
}
后记
这道题的一个重点就是一个点不能作为一条路径,这一点当时卡了 5ab 好久,后来在看题解时才恍然大悟。
做题时,注意题目中的关键是很重要的,这可能就是拉分的关键。
本文来自博客园,作者 5ab,转载请注明链接哦 qwq
博客迁移啦,来看看新博客吧 -> https://5ab-juruo.oier.space/