蓝桥杯第793题——排水系统

发布于:2024-04-04 ⋅ 阅读:(22) ⋅ 点赞:(0)

题目描述

对于一个城市来说,排水系统是极其重要的一个部分。

有一天,小 C 拿到了某座城市排水系统的设计图。排水系统由 n 个排水结点(它们从 1∼n 编号)和若干个单向排水管道构成。每一个排水结点有若干个管道用于汇集其他排水结点的污水(简称为该结点的汇集管道),也有若干个管道向其他的排水结点排出污水(简称为该结点的排出管道)。

排水系统的结点中有 m 个污水接收口,它们的编号分别为 1,2,…,m,污水只能从这些接收口流入排水系统,并且这些结点没有汇集管道。排水系统中还有若干个最终排水口,它们将污水运送到污水处理厂,没有排出管道的结点便可视为一个最终排水口。

现在各个污水接收口分别都接收了 1 吨污水,污水进入每个结点后,会均等地从当前结点的每一个排出管道流向其他排水结点,而最终排水口将把污水排出系统。

现在小 C 想知道,在该城市的排水系统中,每个最终排水口会排出多少污水。该城市的排水系统设计科学,管道不会形成回路,即不会发生污水形成环流的情况。

输入描述

第一行两个用单个空格分隔的整数 n,m。分别表示排水结点数与接收口数量。

接下来 n 行,第 i 行用于描述结点 i 的所有排出管道。其中每行第一个整数 di​ 表示其排出管道的数量,接下来 di​个用单个空格分隔的整数 a1​,a2​,…,adi​​ 依次表示管道的目标排水结点。 保证不会出现两条起始结点与目标结点均相同的管道。

其中,1 ≤ n ≤ 10^5,1 ≤ m ≤ 10,0 ≤ di ​≤ 5。

数据保证,污水在从一个接收口流向一个最终排水口的过程中,不会经过超过 1010 个中间排水结点(即接收口和最终排水口不算在内)。

输出描述

输出若干行,按照编号从小到大的顺序,给出每个最终排水口排出的污水体积。其中体积使用分数形式进行输出,即每行输出两个用单个空格分隔的整数 p,q,表示排出的污水体积为 \frac{p}{q}​ 。要求 p 与 q 互素,q=1 时也需要输出 q。

输入输出样例

示例 1

输入

5 1
3 2 3 5
2 4 5
2 5 4
0
0

输出

1 3
2 3

 解题思路

本题是有向无环图的拓扑排序,而拓扑排序其实就是在图上的搜索,本质上就是dfs和bfs。

这题是典型的有起点有终点的有向图,我们自然而然的想到使用bfs进行遍历,一般像这样的题我们有以下需要关注的点:

  1. 使用邻接表存储点很多的稀疏图;使用ru和chu两个长度为n的数组记录每个点的入度和出度;使用链式队列完成bfs等。
  2. 通常我们可以开辟dp数组记录节点状态,但是本题比较特殊,对于每个点的状态使用p和q组合的一个分数来表示,这样我们就可以利用p和q数组代替dp数组记录节点状态了。

这道题需要我们对分数除法、分数加法、分数约分的过程进行模拟,其内容笔者使用了一个addWater方法进行操作,调用了最大公约数gcd和最小公倍数lcm算法,属于算法有关基础数学知识的内容。

这里需要注意的是,由于分数可能很大,所以我们不仅需要使用long类型进行存储计算,还要对求最小公倍数的时候先除以最大公因数再乘第二个数,否则有可能溢出。

代码题解

具体的代码并没有难以理解的地方,以下是具体的代码:

import java.io.*;
import java.util.*;

public class Main {

    static int n, m;
    static ArrayList<ArrayList<Integer>> edges;
    static int[] ru, chu;
    static long[] p, q;
    static Queue<Integer> qu;
    
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        String[] temp = in.readLine().split(" ");
        n = Integer.parseInt(temp[0]);
        m = Integer.parseInt(temp[1]);
        init();
        for (int i = 1; i <= n; i++) {
            temp = in.readLine().split(" ");
            int t = Integer.parseInt(temp[0]);
            chu[i] = t;
            for (int j = 1; j <= t; j++) {
                int next = Integer.parseInt(temp[j]);
                edges.get(i).add(next);
                ru[next]++;
            }
        }
        for (int i = 1; i <= m; i++) {
            qu.add(i);
            p[i] = 1;
            q[i] = 1;
        }
        while (!qu.isEmpty()) {
            int t = qu.poll();
            for (int e : edges.get(t)) {
                addWater(t, e, chu[t]);
                ru[e]--;
                if (ru[e] == 0 && chu[e] > 0) {
                    qu.add(e);
                }
            }
            p[t] = 0;
            q[t] = 0;
        }
        for (int i = 1; i <= n; i++) {
            if (p[i] != 0) {
                out.print(p[i]);
                out.print(" ");
                out.print(q[i]);
                out.print("\n");
            }
        }
        out.flush();
    }
    public static void addWater(int sourse, int target, int num) {
        long p1 = p[sourse];
        long q1 = q[sourse];
        long t = gcd(p1, num);
        p1 /= t;
        q1 *= num / t;
        
        long p2 = p[target];
        long q2 = q[target];
        if (p2 == 0) {
            p[target] = p1;
            q[target] = q1;
        } else {
            long x = lcm(q1,  q2);
            long t1 = x / q1 * p1 + x / q2 * p2;
            long t2 = x;
            long tt = gcd(t1, t2);
            p[target] = t1 / tt;
            q[target] = t2 / tt;
        }
    }
    public static long gcd(long a, long b) {
        return a % b == 0 ? b : gcd(b, a % b);
    }
    public static long lcm(long a, long b) {
    	return a / gcd(a, b) * b;
    }
    public static void init() {
        edges = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            edges.add(new ArrayList<>());
        }
        p = new long[n + 1];
        q = new long[n + 1];
        ru = new int[n + 1];
        chu = new int[n + 1];
        qu = new LinkedList<>();
    }
}