HDU 5768 Lucky7 数论 中国剩余定理

来源:互联网 发布:口袋妖怪复刻刷钻软件 编辑:程序博客网 时间:2024/06/05 09:11

原题见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 longLL 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 0
原创粉丝点击