zoukankan      html  css  js  c++  java
  • 【题解】食物链 By 5ab as a juruo

    题目

    题目来源:CCF HAOI2016;

    评测地址:LOJ#2060LG3183

    题目描述

    辣鸡 LOJ,图床又炸了

    如图所示为某生态系统的食物网示意图,据图回答第 (1) 小题。

    现在给你 (n) 个物种和 (m) 条能量流动关系,求其中的食物链条数。物种的名称为从 (1)(n) 编号。

    (m) 条能量流动关系形如:

    [a_1quad b_1 ]

    [a_2quad b_2 ]

    [a_3quad b_3 ]

    [cdots ]

    [a_{m-1}quad b_{m-1} ]

    [a_mquad b_m ]

    其中 (a_i)(b_i) 表示能量从物种 (a_i) 流向物种 (b_i)注意单独的一种孤立生物不算一条食物链

    输入格式

    第一行两个整数 (n)(m)

    接下来 (m) 行每行两个整数 (a_i)(b_i) 描述 (m) 条能量流动关系。

    输出格式

    一个整数即食物网中的食物链条数。

    数据规模及约定

    (1le nle 10^5)(0le mle 2 imes 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
    ", ans); // 精准结束
    
    	return 0;
    }
    

    后记

    这道题的一个重点就是一个点不能作为一条路径,这一点当时卡了 5ab 好久,后来在看题解时才恍然大悟。

    做题时,注意题目中的关键是很重要的,这可能就是拉分的关键。

  • 相关阅读:
    浮动 无刷新 div
    vidalia 更换浏览器代理
    vb 主动 点击 a
    Delphi 资料收集
    用Linux的iptables和Python模拟广域网
    框架资源
    div 边框
    Google App Engine
    mac 雪豹 10.6 五国
    Windows Azure Storage (3) Windows Azure Storage Service存储服务之Blob详解(中)
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-haoi2016_lg3183_loj2060.html
Copyright © 2011-2022 走看看