51nod 1352 集合计数(扩展欧几里得)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sxh759151483/article/details/81705976

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

首先很容易可以看出是一个求ax + by = c的所有整数解的题(a = A, b = B, c = n + 1)。

假如先求出x的最小解为x0,那么x的每个解(x1,x2.....xm)之间的差为b / gcd(a,b).

那么只需要知道x的最大解xm就可以得出解的个数ans = (xm - x0)/ (b / gcd);

x的最大解:假如知道y的最小解y0,那么x的最大解xm = (c - y * b) / a;

所以可以先用扩展欧几里得求出x和y的最小解x0、y0

那么ans = (c - yb - ax)/ (a * b / gcd(a,b));

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){
        x=1;y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x,y);
    ll temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}
ll cal(ll a,ll b,ll c) {//求扩展欧几里德
    ll x,y;
    ll gcd = exgcd(a,b,x,y);
    if(c%gcd!=0) return -1;//无解
    x*=c/gcd;
    b/=gcd;
    return (x%b+b)%b;
}


int main()
{
    int T;
    ll n, a, b, c;
    scanf("%d", &T);
    while(T--){
        scanf("%lld%lld%lld", &n, &a, &b);
        c = n + 1;
        ll gcd = __gcd(a, b);
        ll t = b / gcd;
        ll x = cal(a, b, c);
        ll y = cal(b, a, c);
        y = y == 0 ? a / gcd : y;
        x = x == 0 ? b / gcd : x;
        if(x == -1){
            printf("0\n");
            continue;
        }
        int ans = (c * 1.0 - b * y * 1.0 - a * x * 1.0) / (a * t * 1.0) + 1;
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sxh759151483/article/details/81705976