【洛谷P5071】此時此刻的光輝
阿新 • • 發佈:2021-01-19
題目
題目連結:https://www.luogu.com.cn/problem/P5071
珂朵莉給你了一個長為 \(n\) 的序列,有 \(m\) 次查詢,每次查詢一段區間的乘積的約數個數 \(\bmod 19260817\) 的值。
\(n,m\leq 10^5,a_i\leq 10^9\)。
思路
由於 \(2\times 3\times 5\times 7\times 11\times 13\times 17\times 19\times 23\times =6469693230>10^9\),所以 \(10^9\) 以內每個數最多有 \(10\) 個不同的質因子。
我們只需要知道區間 \([l,r]\)
雖然複雜度是 \(O(m\sqrt{n})\),但是毒瘤 lxl 肯定不會放這種演算法過去的。需要一些卡常。
首先我們發現,\(1000\) 以內的質數只有 \(168\) 個,而 \(10^9\) 以內的數除去小於 \(1000\) 的質因子外,最多還有兩個質因子。
所以我們可以設 \(\mathrm{sum}_{i,j}\) 表示序列中前 \(i\) 個數,小於 \(1000\) 的 \(168\) 個質數中,第 \(j\) 個質數的數量。
那麼詢問的時候只需要把大於 \(1000\) 的質因子的貢獻計算出來,然後列舉這 \(168\)
然後不要採用根號的做法求質因子,可以上 Pollard-Rho。同時計算出一個大於 \(1000\) 的質因子後,直接作除法就可以計算出另一個質因子。
那麼我們需要給每一個超過 \(1000\) 的質因子離散化。直接 unordered_map 即可。
然後莫隊維護超過 \(1000\) 的因子即可。寫的正常一些就可以過吧。
時間複雜度 \(O(m\sqrt{n}+168n)\)。
程式碼
C++11 3.55 KB 2.03s 74.64MB
#include <bits/stdc++.h> #define reg register using namespace std; typedef long long ll; typedef long double ld; const int prm[169]={0,2,7,61,3,5,11,13,17,19,23,29,31,37,41,43,47,53,59,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997}; const int N=100010,M=170,MOD=19260817; int n,m,T,tot,maxd,a[N],b[N],c[N],bel[N],inv[N*2],cnt[N*2],sum[M][N]; ll res,ans[N]; map<int,int> id; int read() { int d=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar(); return d; } void write(int x) { if (x>9) write(x/10); putchar(x%10+48); } struct Query { int l,r,id; friend bool operator <(Query x,Query y) { return (bel[x.l]==bel[y.l])?((bel[x.l]&1)?(x.r<y.r):(x.r>y.r)):(bel[x.l]<bel[y.l]); } }ask[N]; ll fpow(ll x,ll k,ll mod=MOD) { ll ans=1; for (;k;k>>=1,x=x*x%mod) if (k&1) ans=ans*x%mod; return ans; } int gcd(int x,int y) { return y?gcd(y,x%y):x; } bool MR(int n) { for (int i=1;i<=3;i++) { if (n==prm[i]) return 1; int m=n-1,power=fpow(prm[i],m,n); while (power==1 && !(m&1)) m>>=1,power=fpow(prm[i],m,n); if (power!=1 && power!=n-1) return 0; } return 1; } int work(int n) { int s=0,t=0,c=rand()%(n-1),lim=1,val=1; for (int i=1;;i++) { t=(1LL*t*t+c)%n; val=1LL*val*abs(s-t)%n; if (!(i%127) || i==lim) { int d=gcd(val,n); if (d>1) return d; if (i==lim) s=t,val=1,lim<<=1; } } } void PR(int n) { if (n<maxd || n<2) return; if (MR(n)) { maxd=n; return; } int p=work(n); while (p>=n) p=work(n); while (!(n%p)) n/=p; PR(n); PR(p); } inline void add(int x) { if (b[x]) res=res*inv[cnt[b[x]]]%MOD*(cnt[b[x]]+=1)%MOD; if (c[x]) res=res*inv[cnt[c[x]]]%MOD*(cnt[c[x]]+=1)%MOD; } inline void del(int x) { if (b[x]) res=res*inv[cnt[b[x]]]%MOD*(cnt[b[x]]-=1)%MOD; if (c[x]) res=res*inv[cnt[c[x]]]%MOD*(cnt[c[x]]-=1)%MOD; } signed main() { srand(1023); n=read(); m=read(); T=sqrt(n)+1; for (int i=1;i<=n;i++) { a[i]=read(); bel[i]=(i-1)/T+1; for (int j=1;j<=168;j++) { sum[j][i]=sum[j][i-1]; for (;!(a[i]%prm[j]) && a[i]>=prm[j];a[i]/=prm[j]) sum[j][i]++; } PR(a[i]); b[i]=maxd; maxd=0; if (b[i] && b[i]!=a[i]) c[i]=a[i]/b[i]; if (b[i]) { if (id.find(b[i])==id.end()) id[b[i]]=++tot; b[i]=id[b[i]]; } if (c[i]) { if (id.find(c[i])==id.end()) id[c[i]]=++tot; c[i]=id[c[i]]; } } cerr<<tot; for (int i=1;i<=m;i++) ask[i]=(Query){read(),read(),i}; sort(ask+1,ask+1+m); inv[0]=inv[1]=cnt[1]=1; for (int i=2;i<=2*n;i++) { inv[i]=(-1LL*MOD/i*inv[MOD%i]+10LL*MOD*MOD)%MOD; cnt[i]=1; } res=1; id[0]=id[1]=0; for (reg int i=1,l=1,r=0;i<=m;i++) { int k=ask[i].id; for (;l>ask[i].l;l--) add(l-1); for (;r<ask[i].r;r++) add(r+1); for (;l<ask[i].l;l++) del(l); for (;r>ask[i].r;r--) del(r); ans[k]=res; for (reg int j=1;j<=168;j++) ans[k]=ans[k]*(sum[j][r]-sum[j][l-1]+1)%MOD; } for (int i=1;i<=m;i++) write(ans[i]),putchar(10); return 0; }