HDU 5768 Lucky7 CRT + 容斥 + 二进制枚举

题目
题意:
对于7的倍数是一个幸运数字,但是给出了n个m和a,如果说一个数x满足\(x \equiv a_i(\mod m_i)\),那就不是幸运数字
\([x,y]\)区间里幸运数字的个数
单考虑\([x,y]\)里7的倍数,y / 7 - (x - 1) / 7
而对于n个条件,二进制枚举配合容斥,求出每个枚举的集合的CRT,但是,这CRT还得是7的倍数,那就用EXCRT,加入\(x\equiv 0(\mod 7)\)这个条件即可
那么求出了最小正整数st,因为CRT是有一个周期\(T = LCM(m_i)\)的,所以根据前缀和,求\([1,n]\)满足\(x = st + tT,\ t\in Z^+\)的数字个数,那么每次的容斥值就是cal(y) - cal(x - 1)

#include <iostream>
#include <cstdio>
#include <vector>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
void ex_gcd(ll a, ll b, ll &d, ll &x, ll &y){
    if(b == 0){
        d = a,x = 1,y = 0;
        return;
    }
    ex_gcd(b, a%b, d, y, x);
    y -= x * (a / b);
}
ll mull(ll a,ll b,ll mod){
    ll ans = 0;
    while(b){
        if(b & 1)ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ans;
}
ll ex_china(vector<ll> a, vector<ll> m, int n){//x = a(mod m)
    ll M = m[0];
    ll ans = a[0];
    ll d, x, y;
    for(int i = 1; i < n; i++){
        ll c = ((a[i] - ans) % m[i] + m[i]) % m[i];
        ex_gcd(M, m[i], d, x, y);
        if(c % d)return -1;//不存在解的情况
        ll mod = m[i] / d;
        x = mull(x, c / d, mod);
        ans += x * M;
        M *= mod;
        ans = (ans % M + M) % M;
    }
    return ans > 0?ans:ans + M;//注意ans是M倍数时输出M
}
ll GCD(ll a, ll b){
    return b == 0 ? a : GCD(b, a % b);
}
ll LCM(ll a, ll b){
    return a / GCD(a, b) * b;
}
ll cal(ll st, ll T, ll n){//[1, n]里
    return (n - st) / T + (n >= st);
}
void solve(){//答案是[x, y]里7的倍数 - n个中国剩余定理
    int n; ll x, y;
    scanf("%d%lld%lld", &n, &x, &y);
    ll a[16], m[16];
    for(int i = 0; i < n; i++)
        scanf("%lld%lld", &m[i], &a[i]);

    ll ans = y / 7 - (x - 1) / 7;//[x, y]里7的倍数
    ll ans1 = 0;
    for(int i = 1; i < (1 << n); i++){//
        int k = 0;
        ll lcm = 1;
        std::vector<ll> mm, aa;
        for(int j = 0; j < n; j++){
            if(i & (1 << j)){
                k++; lcm = LCM(m[j], lcm);
                mm.push_back(m[j]); aa.push_back(a[j]);
            }
        }
        lcm = LCM(lcm, 7);
        mm.push_back(7), aa.push_back(0);
        ll chi = ex_china(aa, mm, k + 1);
        chi = cal(chi, lcm, y) - cal(chi, lcm, x - 1);
        if(k & 1)ans1 += chi;
        else ans1 -= chi;
    }
    printf("%lld\n", ans - ans1);
}
int main(){
    int T;
    scanf("%d", &T);
    for(int tt = 1; tt <= T; tt++)
        printf("Case #%d: ", tt), solve();
    return 0;
}

posted @ 2020-05-06 13:42  Emcikem  阅读(109)  评论(0编辑  收藏  举报