LOJ #3096. 「SNOI2019」数论 脑洞+循环节
碰到这种题第一反应就是找循环节.
我们发现我们就是要求 $a[i]+P \times k=b[i] ( \mod Q)$ 中 $P$ 的 k 的个数.
那么对于 $a[i]$ 来说,最大步数为 $\frac{T-1-a[i]}{P}$.
而我们发现 $a[i]+ P \times k$ 的循环节是 $P \times k=0( \mod Q)$,这个循环节是 $lcm(P,Q)$,而 $k$ 的个数就是 $\frac{lcm(P,Q)}{P}$
枚举 $0$ ~ $Q$,然后对于 $i$ 向 $(i+P)%Q$ 连边,一定会形成若干个环长为 $\frac{Q}{gcd(P,Q)}$ 的环.
那么,让每一个 $a[i]$ 在环上走 $\frac{T-1-a[i]}{P}$ 步,算一下点权和即可.
upd:其实循环节大小都可以不算,直接暴力建图就行了.
code:
#include <bits/stdc++.h> #define ll long long #define N 1000006 #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll T,ans,p[N]; vector<int>s[N],v[N]; int len,P,Q,n,m,a[N],b[N],tx[N],w[N],col[N],pos[N],num; int dfs(int x,int c) { if(col[x]) return 0; col[x]=c; v[col[x]].push_back(x); return tx[x]+dfs((x+P)%Q,c); } int find(int l,int x) { return s[col[x]][pos[x]+l]-s[col[x]][pos[x]]; } int main() { // setIO("input"); int i,j; scanf("%d%d%d%d%lld",&P,&Q,&n,&m,&T); for(i=1;i<=n;++i) scanf("%d",&a[i]); for(i=1;i<=m;++i) scanf("%d",&b[i]); if(P>Q) { swap(P,Q); swap(a,b); swap(n,m); } len=Q/__gcd(P,Q); for(i=1;i<=m;++i) tx[b[i]]=1; for(i=1;i<=n;++i) p[i]=(T-1-a[i])/P; for(i=0;i<Q;++i) { if(!col[i]) ++num,w[num]=dfs(i,num); } for(i=1;i<=num;++i) { for(j=0;j<v[i].size();++j) pos[v[i][j]]=j; int t=v[i].size()-1; for(j=0;j<t;++j) v[i].push_back(v[i][j]); s[i].push_back(tx[v[i][0]]); for(j=1;j<v[i].size();++j) s[i].push_back(s[i][j-1]+tx[v[i][j]]); } for(i=1;i<=n;++i) { ans+=(ll)(p[i]/len)*w[col[a[i]]]; ans+=find(p[i]%len,a[i])+tx[a[i]]; } printf("%lld\n",ans); return 0; }