题意
给定两个长度为
n
n
的数组 ,求区间
[L,R]
[
L
,
R
]
中满足
x
x
是 的倍数且对于
∀i∈[1,n]
∀
i
∈
[
1
,
n
]
,
x≢Ai(modPi)
x
≢
A
i
(
mod
P
i
)
,求
x
x
的个数。
1≤∏i=1nPi≤1018
1
≤
∏
i
=
1
n
P
i
≤
10
18
且
1≤Pi≤105
1
≤
P
i
≤
10
5
思路
直接暴力肯定会超时,但是分别统计满足数对
(Ai,Pi)
(
A
i
,
P
i
)
的数的个数,再直接相加会有重复,而将所有数对
CRT
C
R
T
在一起再统计则会漏算。这时,就需要运用容斥原理。
对于这种取数类型的题目,容斥满足“偶加奇减”的原则,即取偶数个数对时相加,取奇数个数对时相减。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
LL L,R,in_ex;
int n,A[18],P[18];
struct CRT
{
LL A,P;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
return;
}
LL Exgcd(LL A,LL B,LL C)
{
LL k1,k2;
exgcd(A,B,k1,k2);
return (k1*C%B+B)%B;
}
void kase_insert(CRT _)
{
LL res=Exgcd(P,_.P,_.A-A);
A+=P*res;
P=P/gcd(P,_.P)*_.P;
return;
}
};
LL count(CRT sum,LL bnd){return (bnd-sum.A+sum.P)/sum.P;}
void dfs(int k,int cnt,CRT sum)
{
if(k>n)
{
in_ex+=(cnt&1?-1:1)*(count(sum,R)-count(sum,L-1));
return;
}
CRT _sum=sum;
_sum.kase_insert((CRT){A[k],P[k]});
dfs(k+1,cnt+1,_sum);
dfs(k+1,cnt,sum);
return;
}
int main()
{
int T;
scanf("%d",&T);
FOR(Ti,1,T)
{
in_ex=0;
scanf("%d%lld%lld",&n,&L,&R);
FOR(i,1,n)scanf("%d%d",&P[i],&A[i]);
dfs(1,0,(CRT){0,7});
printf("Case #%d: %lld\n",Ti,in_ex);
}
return 0;
}