题解 noip2020 t1
题解
对于这个问题,我们需要分析一个排水系统的节点如何互相传输污水,并计算每个最终排水口排出的污水量。我们可以将问题视为一个有向无环图(DAG),节点之间的边表示污水的流动。
输入解析
- 输入包含:节点数
n
, 污水接收口数m
以及每个节点的出水管道信息。 - 污水接收口(1 到 m)是系统的源节点,每个接收口接收
1
吨的污水。 - 其他节点根据出水管道将污水分配到其他节点。
数据结构
- 使用一个邻接表
e
来表示每个节点的出水管道,和一个数组h
来记录每个节点的出水管道链表头。 - 还需记录每个节点的出水管道数
od
,用于计算污水的分配比例。
重要的变量
num[i]
:表示节点i
中的污水数量(分子部分)。den[i]
:表示节点i
的污水流动分母(用于进行最简分数的计算)。id[i]
:表示节点i
的入度,伴随拜访次数的减少用于拓扑排序。
处理逻辑
- 初始化接收口的污水量为
1
,即num[i] = 1
fori = 1, 2, ..., m
。 - 对于所有节点,读入其出水管道的信息,并更新每个节点的出水管道数量以及其对应的入度。
- 使用一个队列
q
进行拓扑排序(BFS),从接收口开始处理:- 将当前节点的数据均匀分配到所有出水目标节点:
\[num[v] = num[v]+ \frac{num[v]}{od[u]} \] - 处理完所有节点后,找出所有没有出水管道的最终排水口(即入度为
0
的节点)。 - 将每个最终排水口的污水量
num[i]/den[i]
转换为最简分数输出。
输出格式
利用 gcd
函数简化输出格式。如果 num
和 den
为 p
和 q
,则输出两个数 p
和 q
。如果分数为整数,则 q
为 1
。
总结
这个算法在复杂性上大约是 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;
}