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;
}
一直地一直地往前走