HDU 5768 Lucky7 数论 中国剩余定理


原题见HDU 5768

求[l,r]范围内是7的倍数,同时不满足任意一个给定的同余式的数的个数。如范围为[1,100],不满足模3余2或模5余3的7的倍数有7,21,42,49,70,84,91 ,故答案为7.
其中除数都为非7的素数 (105) ,除数的乘积小于 1018 .同余式最多有15个。

分析

所有[l,r]范围内7的倍数减去满足任意一个同余式的数,即得到了答案。而当一个数可以满足多个同余式时,很自然想到了用容斥原理来解决问题。当满足奇数个条件时要减去这个集合,反之则加。

中国剩余定理解决的是同时满足多个同余式的解。因此 215 范围内枚举出各种同余式的组合的解,即可搞出答案了。

//解方程组x=ai(mod mi) mi之间两两互质
int China(int r) {
    int M = 1, ans = 0;
    for (int i = 0; i < r; ++i)
        M *= m[i];
    for(int i = 0;i < r;i++) {
        int N = M/m[i];
        int x, y;
        extend_Euclid(N, m[i], x, y);
        x = (x%m[i]+m[i])%m[i];
        ans = ((ans+a[i]*N%M*x%M)%M + M)%M;
    }
    return ans;
}

特别注意中间解出每个满足的x时,都要取最小的正整数解,否则后面的乘法里会爆long long(就算不爆,这个答案也不对,毕竟m[i]是M的约数,x经过处理会变小)。ans是不断迭代得到的解,是要模M的。
出题人还是善良,在算ans过程中还是可以搞出数据爆long long的,这样就要快速乘法来搞了。注意乘数必须是正数(小小斌死于此hhh)

代码

/*--------------------------------------------
 * File Name: HDU 5768
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2016-07-28 18:59:37
--------------------------------------------*/

#include <bits/stdc++.h>
using namespace std;
#define N 20
#define LL long long
LL a[N], m[N];
int s[N], n;
LL extend_Euclid(LL a, LL b, LL &x, LL &y){
    if(b==0){
        x = 1; y = 0;
        return a;
    }
    LL r = extend_Euclid(b, a%b, y, x);
    y -= a/b*x;
    return r;
}
LL gao(LL x, LL r, LL p){
    return (x-r)/p;
}
LL mult(LL a, LL k, LL m){
    LL res = 0;
    while(k){
        if(k & 1LL)
            res = (res + a) % m;
        k >>= 1;
        a = (a << 1) % m;
    }
    return res;
}
LL China(LL l, LL r)
{
    LL M = 1, ans = 0;
    for (int i = 0; i <= n; ++i) if(s[i]){
        M *= m[i];
    }
    for(int i = 0;i <= n;i++) if(s[i])
    {
        LL Nn = M/m[i];
        LL x, y;
        extend_Euclid(Nn, m[i], x, y);
        x = (x%m[i] + m[i]) % m[i];
        ans = ((ans+mult(a[i]*Nn%M, x, M))%M + M) % M ;
    }
    LL ret = gao(r+M, ans, M) - gao(l-1+M, ans, M);
    return ret;
}
int main(){
    int T, o = 0;
    scanf("%d", &T);
    while(T--){
        LL l, r;
        scanf("%d%lld%lld", &n, &l, &r);
        memset(s, 0, sizeof(s));
        m[n] = 7; a[n] = 0; s[n] = 1;
        for(int i = 0;i < n;i++)
            scanf("%lld%lld", &m[i], &a[i]);
        LL ans = 0;
        int all = 1 << n;
        for(int i = 0;i < all;i++){
            int t = i, k = 0;
            for(int j = 0;j < n;j++){
                s[j] = t & 1;
                t >>= 1;
                k += s[j];
            }
            k = k & 1 ? -1 : 1;
            ans += 1LL * k * China(l, r); 
        }
        printf("Case #%d: %lld\n", ++o, ans);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值