【题解】[NOIP2020] 排水系统 题解
一、题目是什么
化简题意:一个 \(n\) 的点组成的有向图,从 \(1\) ~ \(m\) 个点里面倒 \(1\) 吨水,等分向每一点流去,求每个最终排水口(出度为 \(0\))有多少水。
看数据,很明显地,要高精度或者分数之比来表示水量,分数之表示不知道能不能用 long long,我用的 __int128。
我们先不考虑高精度来解决这个问题。
看入水口,题目有说“并且这些结点没有汇集管道”,说明 \(1\) ~ \(m\) 入度为 \(0\).
“管道不会形成回路”,即保证无环,则不用考虑自己流出去的会返回来。
这不就...
拓扑排序...
没了...
不知道拓扑排序的,,
好的我这就讲。
二、拓扑排序
什么是拓扑排序?
首先排除排序(
拓扑排序,就是,把一张图啪的一下,搞成一个线性序列,使得图中任意一对顶点 \(u\) 和 \(v\),若边 \(<u,v>\in E(G)\),则 \(u\) 在线性序列中出现在 \(v\) 之前。
比如说,有一张学习路线图,在学习一个课程之前要有几个前置课程,现在求出我要怎么学习才能保证学完所有的课程。
最后的结果叫做拓扑序列,这个过程就是拓扑排序。
按照这个拓扑序列学习,能保证在学习到第 \(i\) 个课程时,其前置课程都学过了。
很明显,必须保证无环,不然就会出现,我要学一个课程,但是学这个课程之前需要我要先学完这个课程(禁止套娃
拓扑排序的方法:
- \(\texttt{Step1.}\) 建两个队列 \(q,ans\).
- \(\texttt{Step2.}\) 找到所有入度为 \(0\) 的点,放入 \(q\).
- \(\texttt{Step3.}\) 取出 \(q\) 的头 \(top\),\(ans\) 放入 \(top\).
- \(\texttt{Step4.}\) 删掉 \(top\),所有与 \(top\) 相连的点的入度减 \(1\),如果入度为 \(0\) 则放入 \(q\).
- \(\texttt{Step5.}\) 返回 \(\texttt{Step3}\),直到 \(q\) 为空。
- \(\texttt{Step6.}\) 如果 \(ans\) 的元素个数等于 \(n\) 说明有拓扑序列。
其中 \(ans\) 即为所求
为什么是这样?
每一次 \(q\) 里面放的都是当前情况入度为 \(0\) 的点然后扩展,之后放入 \(ans\).
那为什么?
再拿课程举例子。
我们先学的一定是要没有任何前置课程,或者前置课程已经学过的课程。
这样才能保证能学会这门课。
而第 \(i\) 个点的入度,就相当于,对于第 \(i\) 门课程,你还有多少前置课程没学完。
三、再看题目
RT,是不是是很显然的拓扑排序?
现在比较难的就是水量的表示了,我们用分数表示比较简单,题目的输出也是分数形式。
就直接建一个结构体,就直接就上去搞。
这里直接放代码:
inline void bwr(__int128 x) {
if(x < 0) { putchar('-'); x = -x; }
if(x > 9) bwr(x / 10); putchar(x % 10 + '0');
}
__int128 Gcd_(__int128 a,__int128 b){
if(a % b == 0)return b;
return Gcd_(b,a % b);
}
__int128 Lcm_(const __int128 &a,const __int128 &b){ return a * b / Gcd_(a,b); }
struct Frac{
__int128 up,down;
Frac(){}
Frac(__int128 up_,__int128 down_) : up(up_) , down(down_) {}
}flow_ans[N];
Frac operator + (const Frac &x,const Frac &y){
__int128 tlcm__ = Lcm_(x.down,y.down);
return Frac(tlcm__ / x.down * x.up + tlcm__ / y.down * y.up,tlcm__);
}
Frac operator * (const Frac &x,const Frac &y){
return Frac(x.up * y.up,x.down * y.down);
}
Frac ts_F(const Frac &x){
__int128 tgcd__ = Gcd_(x.up,x.down);
return Frac(x.up / tgcd__,x.down / tgcd__);
}
其中 bwr 函数是用作输出 __int128 类型的数的。
ts_F 函数用来化简分数。
然后?
没有然后了,就这样。
最后理一下思路:
- 拓扑排序求出排水系统拓扑序列
- 在 \(1\) ~ \(m\) 的位置放 \(1\) 吨水
- 按照拓扑序列进行放水
- 输出出度为 \(0\) 的点的答案(分数形式)
这里放一个完整代码:
/*Copyright (C) 2013-2021 LZE*/
#include<bits/stdc++.h>
#define fo(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define INF 0x7fffffff
#define mod 1000000007
#define eps 1e-6
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 1000010;
const int M = 1000010;
ll n,m;
struct GraphEdge{
ll to,next;
}edge[M];
ll head[N] = {0},num = 0;
struct GraphNode{
ll outdegree;
ll indegree;
}node[N];
void add(ll u,ll v){
edge[++num].to = v;
edge[num].next = head[u];
head[u] = num;
node[u].outdegree++;
node[v].indegree++;
}
bool vis[N] = {0};
ll node_cnt = 0;
ll tp_s[N] = {0};
ll top = 0;
void solve(){
queue<ll> q;
for(ll i = 1;i <= n;i++){
if(node[i].indegree == 0)
q.push(i);
}
while(!q.empty()){
ll p = q.front(); q.pop();
vis[p] = true;
tp_s[++top] = p;
for(ll i = head[p];i;i = edge[i].next){
ll to = edge[i].to;
if(!vis[to]){
node[to].indegree--;
if(node[to].indegree == 0){
q.push(to);
}
}
}
}
}
inline void bwr(__int128 x) {
if(x < 0) { putchar('-'); x = -x; }
if(x > 9) bwr(x / 10); putchar(x % 10 + '0');
}
__int128 Gcd_(__int128 a,__int128 b){
if(a % b == 0)return b;
return Gcd_(b,a % b);
}
__int128 Lcm_(const __int128 &a,const __int128 &b){ return a * b / Gcd_(a,b); }
struct Frac{
__int128 up,down;
Frac(){}
Frac(__int128 up_,__int128 down_) : up(up_) , down(down_) {}
}flow_ans[N];
Frac operator + (const Frac &x,const Frac &y){
__int128 tlcm__ = Lcm_(x.down,y.down);
return Frac(tlcm__ / x.down * x.up + tlcm__ / y.down * y.up,tlcm__);
}
Frac operator * (const Frac &x,const Frac &y){
return Frac(x.up * y.up,x.down * y.down);
}
Frac ts_F(const Frac &x){
__int128 tgcd__ = Gcd_(x.up,x.down);
return Frac(x.up / tgcd__,x.down / tgcd__);
}
int main() {
scanf("%lld%lld",&n,&m);
ll u,d;
for(ll i = 1;i <= n;i++){
if(i <= m)flow_ans[i] = Frac(1,1);
else flow_ans[i] = Frac(0,1);
scanf("%lld",&d);
for(ll j = 1;j <= d;j++){
scanf("%lld",&u);
add(i,u);
}
}
solve();
for(ll i = 1;i <= n;i++){
if(node[tp_s[i]].outdegree == 0){
continue;
}
// printf("%lld ",tp_s[i]);
for(ll j = head[tp_s[i]];j;j = edge[j].next){
ll to = edge[j].to;
// printf("%lld ",to);
flow_ans[to] = flow_ans[tp_s[i]] * Frac(1,node[tp_s[i]].outdegree) + flow_ans[to];
flow_ans[to] = ts_F(flow_ans[to]);
}
// printf("\n");
}
for(ll i = 1;i <= n;i++){
if(node[i].outdegree == 0){
bwr(flow_ans[i].up); cout << " "; bwr(flow_ans[i].down);
cout << endl;
}
}
return 0;
}

浙公网安备 33010602011771号