51Nod 1352 集合计数 扩展欧几里得
基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题
给出N个固定集合{1,N},{2,N-1},{3,N-2},...,{N-1,2},{N,1}.求出有多少个集合满足:第一个元素是A的倍数且第二个元素是B的倍数。
提示:
对于第二组测试数据,集合分别是:{1,10},{2,9},{3,8},{4,7},{5,6},{6,5},{7,4},{8,3},{9,2},{10,1}.满足条件的是第2个和第8个。
Input
第1行:1个整数T(1<=T<=50000),表示有多少组测试数据。
第2 - T+1行:每行三个整数N,A,B(1<=N,A,B<=2147483647)
Output
对于每组测试数据输出一个数表示满足条件的集合的数量,占一行。
Input示例
2
5 2 4
10 2 3
Output示例
1
2
思路:
设A的倍数x,B的倍数y,则有Ax+By=N+1
利用exgcd,求Ax+By=gcd(A,B)的解,在求出符合题意最小的x
要注意x==0的情况是不符合题意的
判断第一组解是否符合题意,不符合
然受剩余的部分除以lcm(A,B)即可得到
注:最后除以lcm的证明
证:当x为最小正整数的第一组解符合题意的时候有(设此时为x0,y0):
设增量为k,则有:
此时仍要满足以下等式:
对于第一个式子,显然A*x0满足条件,即需要满足:
同理有:
所以k最小为lcm(A,B)
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll exgcd(ll a, ll b, ll &x, ll&y) { 5 if(!b) { 6 x=1; 7 y=0; 8 return a; 9 } 10 ll ans=exgcd(b,a%b,x,y); 11 ll temp=x; 12 x=y; 13 y=temp-a/b*y; 14 return ans; 15 } 16 int main() { 17 ios::sync_with_stdio(false); 18 ll T,N,A,B,x,y,sum; 19 cin>>T; 20 while(T--) { 21 sum=0; 22 cin>>N>>A>>B; 23 ll g=exgcd(A,B,x,y); 24 if((N+1)%g!=0) { 25 cout<<"0"<<endl; 26 continue; 27 } 28 x=(N+1)/g*x; 29 ll b2=B/g; 30 x=(x%b2+b2)%b2; 31 if(!x) x=b2; 32 y=(N+1-x*A)/B; 33 if(x>=1&&x*A<=N&&y>=1&&y*B<=N&&((x*A+y*B)==(N+1))) 34 sum++; 35 else { 36 cout<<"0"<<endl; 37 continue; 38 } 39 ll lcm; 40 if(!A/g) lcm=A/g*B; 41 else lcm=B/g*A; 42 ll t=(N-x*A)/lcm; 43 if(t>0) sum+=t; 44 cout<<sum<<endl; 45 } 46 return 0; 47 }