P7113 [NOIP2020] 排水系统
有一个排水系统,是有向无环图,编号为 1 1 1到 m m m的点为入水口,这些点都没有入度。没有出度的点为出水口。
现在,每个入水口都接收一吨的污水。污水进入每个点后,会均匀地从这个点的有向边流向其他点。求每个出水口会出多少污水,用最简分数表示,输出分子和分母。
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 10 1\leq n\leq 10^5,1\leq m\leq 10 1≤n≤105,1≤m≤10
数据保证每个点的出度不超过 5 5 5,污水从入水口到出水口的过程中不会经过超过 10 10 10个中间排水点。
首先,很显然要用拓扑。
因为污水从入水口到出水口的过程中不会经过超过 10 10 10个中间排水点,而且保证每个点的出度不超过 5 5 5,所以最终的分母大小不会超过 6 0 11 60^{11} 6011( 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5的最小公倍数是 60 60 60)。这个已经超过 long long \text{long long} long long的范围了,但不超过 1 0 20 10^{20} 1020,所以我们可以用多个变量来存储一个数。
因为最终的分母一定是 6 0 11 60^{11} 6011的因数,所以我们可以将每个分数的分母都设为 6 0 11 60^{11} 6011,那么一开始每个入水口的污水量都是 6 0 11 6 0 11 \dfrac{60^{11}}{60^{11}} 60116011。每次转移时,分子一定能分成对应的若干份,将这个点的分子加到这个点的有向边指向的点即可。因为分母一定,所以只需要存储分子,在输出的时候让分子和 6 0 11 60^{11} 6011约分即可。因为 6 0 11 60^{11} 6011的质因子只有 2 , 3 , 5 2,3,5 2,3,5,所以只需要判断分子和分母是否能同时被 2 , 3 , 5 2,3,5 2,3,5整除,这样的话,不用 gcd \gcd gcd也能约分。
#include
using namespace std;
const long long bl=1e9;
int n,m,ct[100005];
vector<int>pt,v[100005];
queue<int>q;
struct big{
long long v1,v2,v3;
friend big operator+(big x,big y){
big re;
re.v1=(x.v1+y.v1)%bl;
re.v2=(x.v2+y.v2+(x.v1+y.v1)/bl)%bl;
re.v3=x.v3+y.v3+(x.v2+y.v2+(x.v1+y.v1)/bl)/bl;
return re;
}
friend big operator/(big x,int y){
big re;
re.v3=x.v3/y;
re.v2=(x.v3%y*bl+x.v2)/y;
re.v1=((x.v3%y*bl+x.v2)%y*bl+x.v1)/y;
return re;
}
}c,d1,d2,w[100005];
bool pd2(big x){
if(x.v1%2==0) return 1;
return 0;
}
bool pd3(big x){
if((x.v1+x.v2+x.v3)%3==0) return 1;
return 0;
}
bool pd5(big x){
if(x.v1%5==0) return 1;
return 0;
}
void print(big x){
long long wr=x.v3*bl+x.v2;
if(wr==0) printf("%lld",x.v1);
else{
printf("%lld",wr);
int p[15];
for(int i=1;i<=9;i++){
p[i]=x.v1%10;x.v1/=10;
}
for(int i=9;i>=1;i--) printf("%d",p[i]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,k;i<=n;i++){
scanf("%d",&k);
if(k==0) pt.push_back(i);
for(int j=1,x;j<=k;j++){
scanf("%d",&x);
v[i].push_back(x);
++ct[x];
}
}
c=(big){0,279705600,36};
for(int i=1;i<=m;i++) w[i]=c;
for(int i=m+1;i<=n;i++) w[i]=(big){0,0,0};
for(int i=1;i<=m;i++){
q.push(i);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<v[u].size();i++){
w[v[u][i]]=w[v[u][i]]+w[u]/v[u].size();
--ct[v[u][i]];
if(!ct[v[u][i]]) q.push(v[u][i]);
}
}
for(int i=0;i<pt.size();i++){
d1=w[pt[i]];d2=c;
while(pd2(d1)&&pd2(d2)){
d1=d1/2;d2=d2/2;
}
while(pd3(d1)&&pd3(d2)){
d1=d1/3;d2=d2/3;
}
while(pd5(d1)&&pd5(d2)){
d1=d1/5;d2=d2/5;
}
print(d1);
printf(" ");
print(d2);
printf("\n");
}
return 0;
}