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;
}
posted @ 2026-07-08 20:02  Link-Cut_Trees  阅读(10)  评论(1)    收藏  举报