51nod 1352 集合计数

数论

1352 集合计数

可能就我一个傻逼会坚持二分一直wa一个点还一直写下去吧….

不过还好的是最后发现问题症结:乘法爆long long 了…

改了一下__128秒过

不过感觉官方解确实比我想的简单一点

思路:

手玩一下,得到 a ∗ k 1 + b ∗ k 2 = n + 1 a*k1+b*k2=n+1 ak1+bk2=n+1这个不定方程,有约束条件 1 < = a ∗ k 1 < = n 1<=a*k1<=n 1<=ak1<=n,问题就转化为了在该约束下可行解的个数

先特判一下gcd(a,b)与n+1的关系,若无解则答案为0,接下来考虑有解情况

先exgcd得到特解k1’, 对于k1的通解我们则有 k 1 = k 1 ′ + b / g c d ( a , b ) ∗ t k1 = k1'+b/gcd(a,b)*t k1=k1+b/gcd(a,b)t,t为任意整数

考察这个石子,单调递增,所以我们可以二分t,check函数自然就是检查约束条件是否满足

两次二分,分别得出t的左右边界,若如果在该区间内没有满足约束的解,答案则为0,否则答案即为区间长度

Code:

#include<bits/stdc++.h>
#define int __int128
#define rep(i,a,n) for(int i = a; i<=n; i++)
#define pb push_back
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int inf=2147483647;
inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void write(int x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
int exgcd(int a,int b, int &x, int &y){
    if(!b){
        x = 1, y = 0;
        return a;
    }
    else{
        int d = exgcd(b,a%b,x,y);
        int t = x;
        x = y, y = t-(a/b)*y;
        return d;
    }
}
void solve(){
	int n, a, b;
	n=read(); a=read(); b=read();
	int k1, k2;
	int d=exgcd(a,b,k1,k2);
	if((n+1)%d) {write(0);cout<<'\n';return;}
	k1*=(n+1)/d;
	k2*=(n+1)/d;
	int l=-n, r=n;
	int ansl=0, ansr=0;
	while(l<=r){
		int mid=(l+r)/2;
		if(a*k1+a/d*b*mid>=1){
			r=mid-1;
			ansl=mid;
		}
		else l=mid+1;
	}
	l=-inf, r=inf;
	while(l<=r){
		int mid=(l+r)/2;
		if(a*k1+a/d*b*mid<=n){
			l=mid+1;
			ansr=mid;
		}
		else r=mid-1;
	}
	if(a*k1+a/d*b*ansl>n||a*k1+a/d*b*ansr<1) {write(0);cout<<'\n';return;}
	write(ansr-ansl+1);
	cout<<'\n';
}
signed main(){
    //ios;
    int t=1;
   	t=read();
    while(t--){
		solve();
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值