【51Nod三级题】集合计数

题解:

虽然之前做够扩展欧几米得的题,但是印象不深刻,但是今天做了这个题,就是一个应用它的题,看了很多题解,根本看不懂什么意思,看代码,也看不懂,最后在草稿本上再三推导,总算明白了所有的细节。

首先这道题可以直接转化为求ax+by=n+1的多少组解,如果直接枚举的,是不可能过OJ的,因此就要利用数论知识了。

首先将问题转为求ax+by=1的最小x,用exgcd求不解释了,求出最小x和最大公约数d,进而也可以求出最小公倍数c。

接下来问题的核心就是转化为ax+by=n+1时的最小x,首先预处理,如果d!=1,那么这个等式是可以化简的,所以设s=a/d,r=b/d,t=(n+1)/d,当然如果d==1,也有必要这样写。然后就是将ax+by==1的等式右边乘以t,相应的左边的若干组解当中的x,y也要乘以t。如此就是求他的最小x了,又由于在一个最简二元一次方程中,在坐标中根据画图可以得知,是线性的,x的变化量为b,y的变化量为a。因此根据已知的x*t,求x = x*t%r,得出的x就是ax+by=n+1时的最小x,如果x小于0就加上r。所以答案就是就是1+(n-x)/b,减去起始点,后面的距离都是线性的,所以x可以增长多少b就有多少组解了,当然要找到起始的x,因为起始x距离0不一定是d。

还是自己不能活用的自己的知识,回头看就是个简单的模板题+线性方程的处理。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#define ll long long
const int MOD = 1e9+7;
#define CLR(a, b) memset((a), (b),sizeof((a)))
using namespace std;
 
ll exgcd(ll a, ll b, ll &d, ll &x, ll &y){
    if (b == 0){
        x=1;
        y=0;
        d=a;
    }else{
        exgcd(b, a%b, d, y, x);
        y -= (a/b) * x;
    }
}
 
int main(){
    ll t, x, y, r;
    cin >> t;
    while(t--){
        ll n, a, b, d;
        cin >> n >> a >> b;
        exgcd(a, b, d, x, y);
        if((n+1)%d) {
            cout<<"0"<<endl;
        }else{
            x = x*((n+1)/d);
            r = b / d;
            x = x % r;
            while( x <= 0 )
                x += r;
            ll res = n - x * a;
            if (res<0)
                cout << '0' << "\n";
            else
                cout << 1 + res/((a*b)/d) << "\n";
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Lomiss~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值