51Nod 1352 集合计数(扩展欧几里德)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1352

题目大意:

给出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个。

解题思路:

因为第一个元素+第二个元素等于N+1,所以可以列二元一次方程A*x+B*y=N+1求出其中一对解(x0,y0),并得到ra=A/gcd(A,B),rb=B/gcd(A,B),

接下来我们需要求出最小的x,根据方程,我们知道每当y-1则x-A/B(约分后为rb/ra),因为解是整数所以x每次只能减少rb,所以最小的x为x%rb。

于是得到(x1,y1)为x最小,y最大是的情况。接下来求总共有几组解,同理根据上面求最小的x的方法,y每次只能减少ra,x每次只能增加rb。

同时要满足两个条件:①A*x<=n  ②y>0所以最后答案就为1+min(y/ra,(N-A*x)/(A*rb))。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7 //ax+by=gcd(a,b),d为最后求出的gcd(a,b)
 8 void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d){
 9     if(!b){
10         d=a;
11         x=1;
12         y=0;
13         return;
14     }
15     ex_gcd(b,a%b,y,x,d);
16     y=y-a/b*x;
17 }
18 
19 LL cal(LL a,LL b,LL n){
20     LL x,y,d;
21     n++;                  //n+1
22     ex_gcd(a,b,x,y,d);
23     if(n%d)               //ax+by=c,gcd(a,b)|c不成立,则无解
24         return 0;
25     x*=n/d;
26     y*=n/d;
27     LL rb=b/d,ra=a/d;
28     x=(x%rb+rb)%rb;       //求出最小的x,每当y-1则x-a/b(约分后为rb/ra),因为解是整数所以x每次只能减少rb,所以最小的x为x%rb。
29     y=(n-a*x)/b;          //最大的y
30     if(x==0)              //x不能为0
31         x+=rb;
32     if(a*x>n-1)           //无解
33         return 0;
34     return 1+min(y/ra,(n-1-a*x)/(a*rb));//跟求最小的x时同理,但要满足:①a*x<=n-1 ②y>0
35 }
36 
37 int main(){
38     int t;
39     ios::sync_with_stdio(false);
40     cin>>t;
41     while(t--){
42         int n,a,b;
43         cin>>n>>a>>b;
44         cout<<cal(a,b,n)<<endl;
45     }
46     return 0;
47 }
原文地址:https://www.cnblogs.com/fu3638/p/8482238.html