对于一个城市来说,排水系统是极其重要的一个部分。
有一天,小 C 拿到了某座城市排水系统的设计图。排水系统由 nn 个排水结点(它们从 1 sim n1∼n 编号)和若干个单向排水管道构成。每一个排水结点有若干个管道用于汇集其他排水结点的污水(简称为该结点的汇集管道),也有若干个管道向其他的排水结点排出污水(简称为该结点的排出管道)。
排水系统的结点中有 mm 个污水接收口,它们的编号分别为 1, 2, ldots , m1,2,…,m,污水只能从这些接收口流入排水系统,并且这些结点没有汇集管道。排水系统中还有若干个最终排水口,它们将污水运送到污水处理厂,没有排出管道的结点便可视为一个最终排水口。
现在各个污水接收口分别都接收了 11 吨污水,污水进入每个结点后,会均等地从当前结点的每一个排出管道流向其他排水结点,而最终排水口将把污水排出系统。
现在小 C 想知道,在该城市的排水系统中,每个最终排水口会排出多少污水。该城市的排水系统设计科学,管道不会形成回路,即不会发生污水形成环流的情况。
link
题意
题面非常简洁了
给出一张无环图和$m$个源点,每个源点有$1$单位的水,每次会平均分给几个点,求几个终点最后的结果是多少。
思路
直接对于每个源点跑dfs统计答案,管它什么拓扑呢非常的暴力。
但是呢洛谷构造的数据卡掉了unsigned long long
,所以我们换成__int128
(比赛不能用)
这里写一点比赛的小技巧:
如果不会写gcd
,可以去algorithm
头文件里找到__gcd()
(dev
的话可以写了头文件然后直接ctrl
+左键),然后抄出来,那个代码逻辑还是勉强可以看懂的,大概像这样:
ll gcd(ll m,ll n){
while(n!=0){
ll t=m%n;
m=n,n=t;
}
return m;
}
有了gcd
,那么化简分数和分数加法就很简单了,注意显然$\text{lcm}(a,b) =ab \div \gcd(a,b)$
其他注意的细节和思路可见代码注释
代码
#include<bits/stdc++.h>
#define int __int128
//要开__int128
using namespace std;
inline unsigned int read(){//int128没有cin,所以要写快读
unsigned 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-'0';
ch=getchar();
}
return x*f;
}
inline void print(unsigned int x){//输出也一样
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
print(x/10);
putchar(x%10+'0');
}
int gcd(int m,int n){//从algorihm里抄来的gcd
while(n!=0){//是个辗转相除的循环写法
int t=m%n;
m=n,n=t;
}
return m;
}
vector<int> t[100005];//vector存图
int n,m;
struct fra{//fraction,分数
int m,s;//m是分母,s是分子
void cle(){//初始化函数
m=0,s=0;
}
}res[100005],a[100005];
fra ad(fra a,fra b){//将两个分数相加
if(a.m==0) return b;//避免除以0
fra ans;
int gb=a.m*b.m/gcd(a.m,b.m);//求最小公倍数
ans.s=(b.s*(gb/b.m)+a.s*(gb/a.m)),ans.m=gb;
int ga=gcd(ans.s,ans.m);
ans.s/=ga,ans.m/=ga;//这里再化简
return ans;
}
void dfs(int x){
if(t[x].size()==0){//是汇点
res[x]=ad(res[x],a[x]);//res[x]代表汇点x的答案
a[x].m=1,a[x].s=0;//此处清零避免重复加
return;
}
fra tmp=a[x];
tmp.m*=t[x].size();//此处将分母乘出边的个数相当于除以出边的个数
for(int i=0;i<t[x].size();i++){
a[t[x][i]]=ad(a[t[x][i]],tmp);//直接往下加
dfs(t[x][i]);
}
a[x].cle();//此处清零避免重复加
}
signed main(){
// freopen("water.in","r",stdin);
// freopen("water.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) res[i].cle();
for(int i=1;i<=n;i++){//正常建图
int x,y;
x=read();
for(int j=0;j<x;j++) y=read(),t[i].push_back(y);
}
for(int i=1;i<=m;i++){//对于每个源点都要跑一遍dfs,还要注意每个点的值要清零,源点设为1
for(int j=1;j<=n;j++) a[j].cle();
a[i].s=a[i].m=1;
dfs(i);
}
for(int i=1;i<=n;i++){
if(t[i].size()==0){//如果是汇点就输出
print(res[i].s);
printf(" ");
print(res[i].m);
printf("\n");
}
}
return 0;
}
总结
是一道不算太难的题目,感觉出题人本意也不是要让写高精度,放在T1
也不错,至少比CSP
的良心了许多,对于萌新们也挺友好。难度感觉黄题差不多了。
0 条评论