【洛谷5330】[SNOI2019] 數論(數論)
阿新 • • 發佈:2021-06-16
- 給定一個大小為\(n\)的整數集\(A\),一個大小為\(m\)的整數集\(B\)以及三個數\(P,Q,T\)。
- 求\(\sum_{i=0}^{T-1}[(i\% P\in A)\wedge(i\% Q\in B)]\)。
- \(n,m\le10^6\)
數論+建圖
考慮列舉\(a_i\in A\),去計算有多少個\((kP+a_i)\% Q\in B\)。
發現\(P\)固定,每次\(k\)增加\(1\),從一個\(x=(kP+a_i)\%Q\)會變成的\(y=((k+1)P+a_i)\%Q\)是始終不變的。
於是我們建出一張圖,從每個\(x\)向\((x+P)\%Q\)連一條邊,給所有\(b_i\)
顯然,這張圖由若干個環構成,對於每個環記下它的長度以及標記總和,並任選一個點把環斷開依次記下環上的每個點,求出標記的字首和。同時,記錄每個點所在環及在環中的位置。
那麼從一個點\(x\)出發走\(k\)步,假設所在環長為\(cnt\),所在位置為\(rk\)(位置標號為\(0\sim cnt-1\)),它將把整個環遍歷\(\lfloor\frac k{cnt}\rfloor\)次,並額外走過\(rk\sim (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\n",t),0; }