NOIP 2020 T1 排水系统(拓扑排序)

NOIP 2020 T1 排水系统

题解

  • 很显然是拓扑排序,按题意直接模拟复杂度仅仅是 O ( n ) O(n) O(n)的。
  • 但是涉及到分数的加法,通分会爆变量范围吗?
  • 一开始以为最大只是 5 11 ∗ 10 5^{11}*10 51110,连int都不会爆,保险起见还是开了long long。
  • 这样其实是错误的,不能只考虑一条链上,三条深度为 11 11 11的链,在最后的位置合并,分母最大可以到 ( 3 ∗ 4 ∗ 5 ) 11 = 6 0 11 ≈ 2 65 (3*4*5)^{11}=60^{11}≈2^{65} (345)11=6011265,long long都已经无法承受。
  • 如果开了long long,且分数相加时分母取LCM先除后乘,可以得到 90 90 90分;
  • 如果先乘后除,或者分数相加时分母直接相乘, 只可以得到 60 60 60分。
  • 想通过这道题,可以写一个两位数的高精度,或者用long double,注意运算时的精度。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 100010
#define M 500010
#define ld long double
#define e 0.000000000001
int last[N], nxt[M * 2], to[M * 2], len = 0;
int d0[N], d[N];
int q[N];
struct {
	ld x, y;
}s[N];
void add(int x, int y) {
	to[++len] = y;
	nxt[len] = last[x];
	last[x] = len;
}
ld fl(ld x) {
	ld y = floor(x);
	if(y + 1.0 - e <= x) y++;
	return y;
}
ld gcd(ld x, ld y) {
	return (y >= -e && y <= e) ? x : gcd(y, x - fl(x / y) * y);
}
int main() {
	int n, m, i, j, x, y;
	scanf("%d%d", &n, &m);
	for(i = 1; i <= n; i++) {
		scanf("%d", &d0[i]);
		for(j = 1; j <= d0[i]; j++) scanf("%d", &x), d[x]++, add(i, x);
	}
	int l = 0, r = 0;
	for(i = 1; i <= n; i++) s[i].y = 1;
	for(i = 1; i <= n; i++) if(!d[i]) q[++r] = i, s[i].x = 1;
	int tt = 0;
	while(l < r) {
		x = q[++l];
		for(int i = last[x]; i; i = nxt[i]) {
			y = to[i];
			ld X = s[x].x, Y = s[x].y * d0[x];
			ld g = gcd(s[y].y, Y);
			s[y].x = s[y].y / g * X + Y / g * s[y].x;
			s[y].y = s[y].y / g * Y;
			d[y]--;
			if(!d[y]) q[++r] = y;
		}
	}
	for(i = 1; i <= n; i++) if(!d0[i]) {
		ld g = gcd(s[i].x, s[i].y);
		printf("%.0Lf %.0Lf
", s[i].x / g, s[i].y / g);
	}
	return 0;
}

自我小结

  • 考虑问题时还不够全面,同时写代码没有注意细节差异,所谓“差不多”差距可能很大。
  • 需要日常训练积累经验,避免细节大丢分的情况。
原文地址:https://www.cnblogs.com/LZA119/p/14279493.html