思路:拓扑排序,最小公倍数lcm,最大公因数gcd
分数的处理
注意到:从一个点出发,可以分流成 1∼5 支。
所以只要保证任意时刻,水量都是分流数量的倍数即可。
同时,因为本题的分流深度最大为 11(含最末端的一层),所以只要保证开始的水量能被 11 个以内的 1,2,3,4,5 整除,也就是 (1⋅2⋅3⋅4⋅5)11 即可。
但是由于 4 是 2 的倍数,所以只要保证能被 411 整除,就一定能被 211 整除。
所以,水量只需要为 (3⋅4⋅5)11 即可。
由于以上数字过大,所以需要写高精度或使用int128类型。
高精度需要处理加法,除法(高精除以高精,试商需要乘法和减法),gcd(需要减法),此处我选用int128类型,暂不提供高精做法。
关于int128类型的使用,见算法-int128。
1 2 3 4 5 6 7 8 9 10 11
| __int128_t INITIAL = 1;
int main(){ for(int i = 1; i <= 11; i++){ INITIAL *= 3; INITIAL *= 4; INITIAL *= 5; } }
|
排水处理
注意到此题是一个有向无环图,所以只需要从每一个入度为 0 的点出发分流即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| vector<int> connect[N]; int in[N]; __int128_t water[N];
int main(){ for(int i = 1; i <= n; i++) { cin >> d; for(int j = 0; j < d; j++){ cin >> x; in[x]++; connect[i].push_back(x); } } queue<int> q; for(int i = 1; i <= m; i++){ q.push(i); water[i] = INITIAL; } while(q.size()){ int from = q.front(); q.pop(); const __int128_t flown = water[from] / connect[from].size(); for(int to : connect[from]){ in[to]--; water[to] += flown; if(!in[to] && connect[to].size()) q.push(to); } water[from] = 0; } }
|
约分
现在有了每个出水口的水量(分子)和初始水量(分母),它们的gcd就是要约掉的数。
1 2
| __int128_t gcd = __gcd(INITIAL, water[i]); cout << to_str(water[i] / gcd) << ' ' << to_str(INITIAL / gcd) << endl;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include <bits/stdc++.h> #define set(x, y) memset(x, y, sizeof x)
using namespace std; using ll = long long;
const int inf = 0x7f7f7f7f;
const int N = 1e5 + 10; const int M = 6;
vector<int> connect[N]; vector<int> prn;
int in[N];
__int128_t water[N];
__int128_t INITIAL = 1;
string to_str(__int128_t x){ if(x < 10) return to_string((int)x); else return to_str(x / 10) + to_string((int)(x % 10)); }
int main() { #ifndef ONLINE_JUDGE freopen("water1.in", "r", stdin); freopen("water.out", "w", stdout); #endif int n, m; cin >> n >> m; int d, x; for(int i = 1; i <= 11; i++){ INITIAL *= 3; INITIAL *= 4; INITIAL *= 5; } for(int i = 1; i <= n; i++) { cin >> d; for(int j = 0; j < d; j++){ cin >> x; in[x]++; connect[i].push_back(x); } } queue<int> q; for(int i = 1; i <= m; i++){ q.push(i); water[i] = INITIAL; } for(int i = 1; i <= n; i++){ if(!connect[i].size()) prn.push_back(i); } while(q.size()){ int from = q.front(); q.pop(); const __int128_t flown = water[from] / connect[from].size(); for(int to : connect[from]){ in[to]--; water[to] += flown; if(!in[to] && connect[to].size()) q.push(to); } water[from] = 0; } for(int i : prn){ __int128_t gcd = __gcd(INITIAL, water[i]); cout << to_str(water[i] / gcd) << ' ' << to_str(INITIAL / gcd) << endl; } return 0; }
|