P7113 [NOIP2020] 排水系统 题解
思路
因为没有环,所以本题可以用拓扑排序处理。
设第 \(i\) 个管道流过的水占总水量的 \(\frac{p_i}{q_i}\)。若第 \(v\) 个管道有 \(k\) 个儿子,则它的水量会被平分成 \(k\) 份,每个儿子会收到 \(1\) 份。化简一下得:
\[\displaystyle \frac{p_u}{q_u} = \begin{aligned} \sum_{v \; \in \; fa_u} \displaystyle \frac{p_v}{q_v \times |son_v|} \end{aligned}
\]
那就这样拓扑,同时求答案。注意分数要化简,而且需要高精度,或者用 __int128。
代码
#include <bits/stdc++.h>
#define int __int128
using namespace std;
const int N = 1e5 + 5;
int n, m;
int deg[N], p[N], q[N];
vector<int> tr[N];
int read()
{
int x = 0,f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = x * 10 + ch - 48, ch = getchar();
return x * f;
}
void write(int x)
{
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
int gcd(int x, int y)
{
if (y == 0) return x;
return gcd(y, x % y);
}
pair<int, int> addFraction(int a, int b, int x, int y)
{
if (a == 0) return (make_pair(x, y));
int t = b / gcd(b, y) * y;
int xx = t / b * a + t / y * x, yy = t;
int g = gcd(xx, yy);
xx /= g, yy /= g;
return (make_pair(xx, yy));
}
signed main()
{
n = read(), m = read();
for (int i = 1, x; i <= n; i++)
{
x = read();
for (int j = 1, y; j <= x; j++)
{
y = read();
tr[i].push_back(y);
deg[y]++;
}
}
queue<int> Q;
for (int i = 1; i <= n; i++)
{
if (!deg[i])
{
Q.push(i);
p[i] = 1;
}
}
for (int i = 1; i <= n; i++)
q[i] = 1;
while (Q.size())
{
int u = Q.front();
Q.pop();
for (auto v : tr[u])
{
pair<int, int> tmp = addFraction(p[v], q[v], p[u], q[u] * tr[u].size());
p[v] = tmp.first, q[v] = tmp.second;
deg[v]--;
if (!deg[v]) Q.push(v);
}
}
for (int i = 1; i <= n; i++)
{
if (!tr[i].size())
{
int g = gcd(p[i], q[i]);
p[i] /= g, q[i] /= g;
write(p[i]);
printf(" ");
write(q[i]);
printf("\n");
}
}
return 0;
}

浙公网安备 33010602011771号