HDU 5768 (Lucky7 中国剩余定理+容斥原理)

题目链接
问区间[x,y]能被7整除,且不可以 %pi=ai,

因为满足任意一组pi和ai,即可使一个“幸运数”被“污染”,我们可以想到通过容斥来处理这个问题。当我们选定了一系列pi和ai后,题意转化为求[x,y]中被7整除余0,且被这一系列pi除余ai的数的个数,可以看成若干个同余方程联立成的一次同余方程组。然后我们就可以很自然而然的想到了中国剩余定理。需要注意的是,在处理中国剩余定理的过程中,可能会发生超出LongLong的情况,需要写个类似于快速幂的快速乘法来处理。

使用剩余定理计算出来的是t在模M(M是某次计算中CRT中的M)的下一个解集,然后在统计区间[x,y]
中有多少个t+k*M <=y就好了(k>=0的整数),这一部分使用容斥原理计算,对于给出的n个条件,我们需要枚举所有的组合方式,注意到n<=15可以使用二进制枚举子集。 但是枚举子集的结果是会有重叠的,比如(<7,0>,<3,2>,<5,3>)三个点,枚举的时候,{<7,0>,<3,2>},{<7,0>,<5,3>},{<7,0>,<3,2>,<5,3>}三个集合
结果是模21下的14,模35下的28,模105下的98,
那么在[1,100]中,三个集合对应的解集就是
{14,35,56,77,98}
{28,63,98}
{98}
可以发现类似,求1-100中,3的倍数与4的倍数有几个,就是3的倍数加上4的倍数减去3,4共同的倍数
这里使用容斥原理做一下,就解决了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<bits/stdc++.h>
#include<map>
#include<string>
#include<cstdlib>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define LL long long
#define pb push_back

#define For(i,j,k) for(int i=(j);i<k;i++)
#define lowbit(i) (i&(-i))
#define _(x) printf("%d\n",x)

const int maxn = 15+200;
const int inf  = 1 << 28;

LL qsm(LL a,LL b,LL M){
    LL ans = 0;
    while(b){
        if(b&1)ans=(ans + a) % M;
        a=(a + a) % M;
        b>>=1;
    }
    return ans % M;
}
void gcd(LL a,LL b,LL &d,LL &x,LL &y){
    if(b==0){y=0;x=1;d=a;}
    else {gcd(b,a%b,d,y,x);y-=x*(a/b);}
}

pair<LL,LL> CRT(LL a[],LL m[],int n){
    LL M=1;
    for(int i=0;i<n;i++)M*=m[i];
    LL ans = 0;
    LL x,y,d;
    for(int i=0;i<n;i++){
        LL w = M / m[i];
        gcd(m[i], w, d, d, y);
        ans = (ans + qsm(y*w,a[i],M)) % M;
    }
    if(ans < 0)ans += M;
    return make_pair(ans % M, M);
}

LL a[maxn],p[maxn];

LL aa[maxn],pp[maxn];

LL x,y;

LL work(int n){
    LL ans = 0;
    int N = 0;
    for(int i=1;i<(1<<n);i++){
        N=0;
        for(int j=0;j<n;j++){
            if((i&(1<<j))){
                aa[N]=a[j];pp[N++]=p[j];
            }
        }
        aa[N]=0LL;pp[N++]=7LL;

        pair<LL,LL> tmp = CRT(aa,pp,N);
        LL t = tmp.first,M = tmp.second;
        if(t==0)continue;

        if(N&1){
            if(y>=t)ans-=(y-t)/M+1;
            if(x-1>=t)ans+=(x-1-t)/M+1;
        }
        else {
            if(y>=t)ans+=(y-t)/M+1;
            if(x-1>=t)ans-=(x-1-t)/M+1;
        }

    }
    return ans;
}

int main(){
    int T;scanf("%d",&T);int cas=1;
    while(T--){
        int n;scanf("%d%lld%lld",&n,&x,&y);
        for(int i=0;i<n;i++){
            scanf("%lld%lld",&p[i],&a[i]);
        }
        LL sum = y/7-(x-1)/7;
        LL t = work(n);

        printf("Case #%d: %lld\n",cas++,sum-t);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值