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

题目描述


分析

这一道题和小凯的疑惑那一道题比较像
对于两个数 \(a,b\),如果 \(gcd(a,b)=1\) ,那么它们不能表示的最大的数是 \(a \times b -a -b\)
对于大于 \(a \times b -a -b\) 的数,都可以表示出来
那么我们现在的问题就是求出小于等于 \(a \times 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\times 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\n",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\n",ans);
		} else {
			if(q/now>=haha) ans=ans+(q/now-haha);
			ans=q-ans;
			printf("%lld\n",ans);
		}
	}
	return 0;
}
posted @ 2020-10-22 10:32  liuchanglc  阅读(112)  评论(0编辑  收藏  举报