联赛模拟测试20 A. Simple (数学)

题目描述


分析

这一道题和小凯的疑惑那一道题比较像
对于两个数 (a,b),如果 (gcd(a,b)=1) ,那么它们不能表示的最大的数是 (a imes b -a -b)
对于大于 (a imes b -a -b) 的数,都可以表示出来
那么我们现在的问题就是求出小于等于 (a imes b -a -b) 的数中不能被表示出来的数的个数
观察数据范围,我们可以发现这两个数中一个是 (10^9) 级别的,另一个是 (10^5) 级别的
我们就可以从这里去找突破口,把序列依次划分为若干块,其中每一块的长度为 (b)

我们分别求出 ([0,a*b-a-b]) ([b,a*b-a-b]) ([2b,a*b-a-b]) ...中可以用 (a,b) 表示的数的个数
这样的数的数量为 (sum_{i=0}^{i*b<=a*b-a-b}((a*b-a-b-i*b)/a+1))
因为 (a,b) 是互质的,所以这样划分不会重复
我们拿最大值减去合法的方案就是不合法的方案
对于 (a,b) 不互质的情况,我们把 (a,b,q) 同时除以 (gcd(a,b))
这样的出的合法的方案数显然是相同的,因为去掉的都是不合法的方案
时间复杂度 (O(n imes t))

代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
typedef long long ll;
int t;
ll a,b,q,ans,now,mmax,haha,ha;
ll gcd(ll aa,ll bb){
	if(bb==0) return aa;
	return gcd(bb,aa%bb);
}
int main(){
	freopen("simple.in","r",stdin);
	freopen("simple.out","w",stdout);
	scanf("%d",&t);
	while(t--){
		ans=0;
		scanf("%lld%lld%lld",&a,&b,&q);
		if(a==b){
			printf("%lld
",q-q/a);
			continue;
		}
		now=gcd(a,b);
		a/=now,b/=now;
		haha=a*b-a-b;
		mmax=std::min(haha,q/now);
		if(a>b) std::swap(a,b);
		for(ll i=1;i*b<=mmax;i++){
			ans+=(mmax-i*b)/a;
		}
		ans+=mmax/a;
		ans+=mmax/b;
		if(now==1){
			ans=mmax-ans;
			printf("%lld
",ans);
		} else {
			if(q/now>=haha) ans=ans+(q/now-haha);
			ans=q-ans;
			printf("%lld
",ans);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/liuchanglc/p/13854970.html