P5330 [SNOI2019] 数论
题意
给出正整数 \(P,Q,T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),现在需要求:\(\sum_{i=0}^{T-1}[(i\bmod P) \in A \land (i\bmod Q) \in B]\)。
\(n\le10^6\),\(p,q\le10^6\),\(T\le10^18\)。
思路
设 \(g=\gcd(P,Q)\),对于一对 \((i\in A,j\in B)\),如果 \(i\not\equiv j\pmod g\),则不存在 \(x\) 满足 \(x\equiv i\pmod P,x\equiv j\pmod Q\),反之,如果 \(i\equiv j\pmod g\),则在 \([0,lcm(P,Q))\) 中存在且仅存在一个 \(x\) 满足 \(x\equiv i\pmod P,x\equiv j\pmod Q\)。把 \(\bmod g\) 相等的值领出来分开计算。按照 \(lcm(P,Q)\) 把 \([0,T)\) 分成很多块,整块是好计算的,考虑计算散块。以 \(P=4,Q=6\) 为例,计算 \(\equiv1\pmod2\) 的部分。
| mod 4 | mod 6 |
|---|---|
| 1 | 1 |
| 3 | 3 |
| 1 | 5 |
| 3 | 1 |
| 1 | 3 |
| 3 | 5 |
发现当 \(\equiv1\pmod4\) 时 \(\bmod6\) 的结果是 \(1,5,3,\dots\),当 \(\equiv3\pmod4\) 时 \(\bmod6\) 的结果是 \(3,1,5,\dots\),找到规律:当模 \(P\) 的余数增加 \(g\) 时,模 \(Q\) 的最小循环节会把最后几位搬到前面,而每次被搬到前面的数的个数 mis 可以通过在环上找到 \((i+g)\bmod Q\) 的位置来计算,即 mis=环长-pos。
根据这个规律,我们给每个值标号,枚举模 \(P\) 的余数 \(v\),开个桶判断是否在 \(A\) 中,同时在环上用前缀和快速统计连续区间内有多少个值在 \(B\) 中,从而计算散块中的答案。
代码
做法非常神秘,但祂是线性的,对吧。
// Problem: P5330 [SNOI2019] 数论
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5330
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=1000010;
int p,q,n,m,sum[maxn];
LL t,ans;
bool ina[maxn],inb[maxn],app[maxn];
vector<int>vta[maxn],vtb[maxn],vt;
signed main(){
read(p,q,n,m,t);
int g=__gcd(p,q);
LL l=1ll*p*q/g;
for(int i=1;i<=n;i++){
int v;read(v);
vta[v%g].push_back(v);
ina[v]=1;
}
for(int i=1;i<=m;i++){
int v;read(v);
vtb[v%g].push_back(v);
inb[v]=1;
}
for(int i=0;i<g;i++){
ans+=t/l*vta[i].size()*vtb[i].size();
vt.clear();
vt.push_back(i);
LL nw=i;
while(1){
nw+=p;
if(nw%q==vt[0]) break;
vt.push_back(nw%q);
}
sum[0]=inb[vt[0]];
for(int i=1;i<vt.size();i++) sum[i]=sum[i-1]+inb[vt[i]];
int mis;
for(int wz=1;wz<vt.size();wz++) if(vt[wz]==(i+g)%q) mis=vt.size()-wz;
LL sy=t%l;
if(sy==0) continue;
sy--;
int w=vt.size()+mis;
for(LL v=i;1;v+=g){
if(app[v%p]) break;app[v%p]=1;
w-=mis;
w=(w+vt.size())%vt.size();
int cnt=(sy-v)/p+1;
if(v>sy) cnt=0;
if(cnt<=0) continue;
int gh=vt.size()-w;
if(ina[v]==0) continue;
if(gh>=cnt) ans+=sum[w+cnt-1]-(w?sum[w-1]:0);
else ans+=sum[vt.size()-1]-(w?sum[w-1]:0)+sum[cnt-gh-1];
}
for(LL v=i;1;v+=g){
if(app[v%p]==0) break;
app[v%p]=0;
}
}
write(ans);
return 0;
}

浙公网安备 33010602011771号