题目链接
问区间[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;
}