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;
}
posted @ 2025-07-19 22:03  lucasincyber  阅读(9)  评论(0)    收藏  举报