题解 noip2020 t1

题解

对于这个问题,我们需要分析一个排水系统的节点如何互相传输污水,并计算每个最终排水口排出的污水量。我们可以将问题视为一个有向无环图(DAG),节点之间的边表示污水的流动。

输入解析

  • 输入包含:节点数 n, 污水接收口数 m 以及每个节点的出水管道信息。
  • 污水接收口(1 到 m)是系统的源节点,每个接收口接收 1 吨的污水。
  • 其他节点根据出水管道将污水分配到其他节点。

数据结构

  • 使用一个邻接表 e 来表示每个节点的出水管道,和一个数组 h 来记录每个节点的出水管道链表头。
  • 还需记录每个节点的出水管道数 od,用于计算污水的分配比例。

重要的变量

  • num[i]:表示节点 i 中的污水数量(分子部分)。
  • den[i]:表示节点 i 的污水流动分母(用于进行最简分数的计算)。
  • id[i]:表示节点 i 的入度,伴随拜访次数的减少用于拓扑排序。

处理逻辑

  1. 初始化接收口的污水量为 1,即 num[i] = 1 for i = 1, 2, ..., m
  2. 对于所有节点,读入其出水管道的信息,并更新每个节点的出水管道数量以及其对应的入度。
  3. 使用一个队列 q 进行拓扑排序(BFS),从接收口开始处理:
    • 将当前节点的数据均匀分配到所有出水目标节点:

    \[num[v] = num[v]+ \frac{num[v]}{od[u]} \]

  4. 处理完所有节点后,找出所有没有出水管道的最终排水口(即入度为 0 的节点)。
  5. 将每个最终排水口的污水量 num[i]/den[i] 转换为最简分数输出。

输出格式

利用 gcd 函数简化输出格式。如果 numdenpq,则输出两个数 pq。如果分数为整数,则 q1

总结

这个算法在复杂性上大约是 O(n + m),适用于囊括的输入范围。我们通过拓扑排序的方式确保污水的流动按序处理,避免环路问题,并确保最后的每个排水口的污水量以最优形式输出。

注意事项

这道题的数据范围颇大,正解是用高精度,不过_int128问题也不大

代码

#include <bits/stdc++.h>  
#define I128 __int128_t  

using namespace std;  

const int MAX_NODES = 100001;  
const int MAX_EDGES = 500001;  

struct Nd {  
    int nxt, to;  
};  

I128 gcd(I128 a, I128 b) {  
    while (b) {  
        I128 t = b;  
        b = a % b;  
        a = t;  
    }  
    return a;  
}  

void addE(int u, int v, vector<Nd>& e, vector<int>& h, int& c) {  
    e[++c] = { h[u], v };  
    h[u] = c;  
}  

void updFr(I128& n1, I128& d1, I128 n2, I128 d2) {  
    I128 d = d1 * d2 / gcd(d1, d2);  
    n1 *= d / d1;  
    n2 *= d / d2;  
    n1 += n2;  
    d1 = d;  
}  

template<typename T>  
void rd(T& v) {  
    v = 0;  
    char ch = getchar();  
    while (ch < '0' || ch > '9') ch = getchar();  
    while (ch >= '0' && ch <= '9') {  
        v = (v << 3) + (v << 1) + (ch ^ 48);  
        ch = getchar();  
    }  
}  

void wr(I128 v) {  
    if (v > 9) wr(v / 10);  
    putchar(v % 10 + '0');  
}  

int main() {  
    int n, m, c = 0;  
    vector<int> h(MAX_NODES, 0), od(MAX_NODES, 0), id(MAX_NODES, 0);  
    vector<I128> num(MAX_NODES, 0), den(MAX_NODES, 1); 
    queue<int> q;  

    rd(n);  
    rd(m);  

    for (int i = 1; i <= m; ++i) {  
        num[i] = 1;  
        q.push(i);  
    }  

    vector<Nd> e(MAX_EDGES);  

    for (int i = 1; i <= n; ++i) {  
        int d;  
        rd(d);  
        den[i] = 1;  
        od[i] += d;  

        for (int j = 1; j <= d; ++j) {  
            int x;  
            rd(x);  
            addE(i, x, e, h, c);  
            id[x]++;
        }  
    }  

    while (!q.empty()) {  
        int u = q.front();  
        q.pop();  
        for (int i = h[u]; i; i = e[i].nxt) {  
            int v = e[i].to;  
            updFr(num[v], den[v], num[u], od[u] * den[u]);  
            id[v]--; 

            if (id[v] == 0) {  
                q.push(v);  
            }  
        }  
    }  

    for (int i = 1; i <= n; ++i) {  
        if (od[i] == 0) {  
            I128 g = gcd(num[i], den[i]);  
            wr(num[i] / g);  
            putchar(' ');  
            wr(den[i] / g);  
            putchar('\n');  
        }  
    }  

    return 0;  
}
posted @ 2024-11-12 08:38  健康铀  阅读(8)  评论(0编辑  收藏  举报