NOIP2020 排水系统

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 1n105,1m10

数据保证每个点的出度不超过 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也能约分。

code

#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;
}

你可能感兴趣的:(题解,题解,c++)