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

传送门

题意

分析

即求ax+by=n+1的所有正整数对解

我们可以求出最小的x 然后每lcm(a,b)个数,又满足ax+by==n+1 且 x,y均为整数
所以就是裸的扩展欧几里得算法了

对上述做一些说明
1.如何求最小的x?
做一遍ex_gcd(a,b,x,y),得到x,y,让x±(b/gcd(a,b))得到最小正整数x
2.为什么是lcm个数
\(a*(x+b/gcd(a,b)*t)+b*(y-a/gcd(a,b)*t)=n+1\)
\(a*(x+lcm)+b*(y-lcm)=n+1\)
3.注意求个数时(n+1-ax)%lcm非0要加1

代码

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

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))

int t;
ll n,a,b;
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
void ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1,y=0;return ;
    }
    ex_gcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}
int main()
{
    for(scanf("%d",&t);t--;)
    {
        scanf("%lld %lld %lld",&n,&a,&b);
        n++;
        ll g=gcd(a,b);
        if(n%g) { puts("0");continue; }
        ll x,y;
        ex_gcd(a,b,x,y);//求出的是a*x+b*y=gcd(a,b)
        ll lcm=a/g*b;
        x*=n/g;
        ll B=b/g;
        x=(x%B+B)%B;
        if(x==0) x+=B;
        y=n-x*a;
        if(y<0) puts("0");
        else printf("%lld\n",(y%lcm)?y/lcm+1:y/lcm);
    }
    return 0;
}
posted @ 2017-09-27 20:37  遗风忘语  阅读(123)  评论(0编辑  收藏  举报