- 给定一个大小为(n)的整数集(A),一个大小为(m)的整数集(B)以及三个数(P,Q,T)。
- 求(sum_{i=0}^{T-1}[(i\% Pin A)wedge(i\% Qin B)])。
- (n,mle10^6)
数论+建图
考虑枚举(a_iin A),去计算有多少个((kP+a_i)\% Qin B)。
发现(P)固定,每次(k)增加(1),从一个(x=(kP+a_i)\%Q)会变成的(y=((k+1)P+a_i)\%Q)是始终不变的。
于是我们建出一张图,从每个(x)向((x+P)\%Q)连一条边,给所有(b_i)打上标记,则问题就变成从(a_i)出发走(lfloorfrac{T-1-a_i}{P} floor)步能经过多少关键点。
显然,这张图由若干个环构成,对于每个环记下它的长度以及标记总和,并任选一个点把环断开依次记下环上的每个点,求出标记的前缀和。同时,记录每个点所在环及在环中的位置。
那么从一个点(x)出发走(k)步,假设所在环长为(cnt),所在位置为(rk)(位置标号为(0sim cnt-1)),它将把整个环遍历(lfloorfrac k{cnt} floor)次,并额外走过(rksim (rk+k)\%cnt)的所有点,后者利用我们预处理出的标记前缀和可以方便计算。
代码:(O(P+Q))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define LL long long
using namespace std;
int n,m,P,Q,a[N+5],b[N+5];LL T;
int id[N+5],rk[N+5],s[N+5],cnt[N+5],tot[N+5];vector<int> w[N+5];
int main()
{
RI i,j;for(scanf("%d%d%d%d%lld",&P,&Q,&n,&m,&T),i=1;i<=n;++i) scanf("%d",a+i);
for(i=1;i<=m;++i) scanf("%d",b+i),s[b[i]]=1;//打标记
RI x,y,c=0;for(i=0;i^Q;++i) if(!id[i])
{
x=i,++c;W(!id[x]) w[id[x]=c].push_back(x),rk[x]=cnt[c]++,x=(x+P)%Q;//遍历环,依次记录所有点
for(j=1;j^cnt[c];++j) s[w[c][j]]=s[w[c][j]]+s[w[c][j-1]];tot[c]=s[w[c][cnt[c]-1]];//求出标记的前缀和,记录整个环的标记总和
}
LL k,t=0;for(i=1;i<=n;++i) k=(T-1-a[i])/P,x=id[a[i]%Q],y=rk[a[i]%Q],//从第x个环的第y位出发走k步
t+=k/cnt[x]*tot[x],k%=cnt[x],t+=(y+k<cnt[x]?s[w[x][y+k]]:tot[x]+s[w[x][y+k-cnt[x]]])-(y?s[w[x][y-1]]:0);//遍历整环k/cnt[x]遍,额外走y~(y+k)%cnt[x]所有点
return printf("%lld
",t),0;
}