洛谷 P5071 - [Ynoi2015] 此時此刻的光輝(莫隊)
阿新 • • 發佈:2021-10-04
莫隊+數論中一些輕微降低複雜度的小技巧
中質因子的貢獻,我們直接字首和掃一遍即可計算,複雜度 \((n+q)\pi(\sqrt[3]{a_i})\),對於 \(>\sqrt[3]{a_i}\) 的質因子的貢獻就按照上面的方法進行莫隊。注意到一個數最多有 \(2\) 個 \(>\sqrt[3]{a_i}\) 的因子,因此莫隊的 \(\omega\) 就變成了常數,總複雜度也進而變為 \(\dfrac{\sqrt{a_i}}{\ln\sqrt{a_i}}·n+(n+q)\pi(\sqrt[3]{a_i})+(n+q)\sqrt{n}\)。
一道其實算得上常規的題,寫這篇題解是為了總結一些數論中輕微(?)優化複雜度的技巧。
首先感性理解可以發現該問題強於區間數顏色問題,無法用常用的 log 資料結構維護,因此考慮分塊/莫隊。顯然這題莫隊比較好些對吧?顯然我們要對每個質因子計算一遍它在 \([l,r]\) 中的出現次數對吧?涉及質因子就要分解質因數對吧?莫隊時候新添一個元素很明顯就要列舉它的每個質因子,然後計算新添的貢獻對吧?線性預處理乘法逆元以後,複雜度就變成了 \(n\sqrt{a_i}+(n+q)\sqrt{n}\omega(a_i)\),其中左邊的分解質因數的複雜度,右邊是莫隊的複雜度,對吧?恭喜你,你完美地獲得了 TLE 的好成績。
考慮優化。加號左右兩部分顯然都不可行,都要進行優化。左邊一部分相對比較容易:考慮預處理出 \([1,\sqrt{a_i}]\) 中所有質因子,這樣每次分解不必 \([1,\sqrt{a_i}]\) 跑一遍,只用跑其中 \(\mathcal O(\dfrac{\sqrt{a_i}}{\ln\sqrt{a_i}})\) 個質因子即可,這樣左邊的複雜度就變成了 \(\dfrac{\sqrt{a_i}}{\ln\sqrt{a_i}}·n\)。再考慮右邊,注意到在每個數所有不同質因子中,大部分都比較小,因此考慮用類似於根號分治的方法,我們預處理 \([1,\sqrt[3]{a_i}]\) 中所有質因子,對於 \([1,\sqrt[3]{a_i}]\)
const int MAXN=1e5; const int MAXV=31630; const int LIM=1000; const int PI_LIM=220; const int MAXX=1e6; const int BLK=316; int n,qu,a[MAXN+5]; int pr[MAXV/6+5],prcnt=0,vis[MAXV+5]; void sieve(int n){ for(int i=2;i<=n;i++){ if(!vis[i]) pr[++prcnt]=i; for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){ vis[pr[j]*i]=1;if(i%pr[j]==0) break; } } } int c[MAXN+5][3],cc[MAXN+5]; int sum[MAXN+5][PI_LIM+5],inv[MAXX+5]; int res[MAXN+5],bel[MAXN+5],L[BLK+5],R[BLK+5],blk_sz,blk_cnt; int key[MAXN*2+5],uni[MAXN*2+5],ccnt=0,num=0; struct qry{ int l,r,id; bool operator <(const qry &rhs){ if(bel[l]^bel[rhs.l]) return bel[l]<bel[rhs.l]; else if(bel[l]&1) return r<rhs.r; else return r>rhs.r; } } q[MAXN+5]; int cnt[MAXN*2+5],mul=1; void ins(int x){ for(int i=1;i<=cc[x];i++){ mul=1ll*mul*inv[cnt[c[x][i]]+1]%MOD; cnt[c[x][i]]++; mul=1ll*mul*(cnt[c[x][i]]+1)%MOD; } } void del(int x){ for(int i=1;i<=cc[x];i++){ mul=1ll*mul*inv[cnt[c[x][i]]+1]%MOD; cnt[c[x][i]]--; mul=1ll*mul*(cnt[c[x][i]]+1)%MOD; } } int main(){ scanf("%d%d",&n,&qu);sieve(MAXV); blk_sz=(int)sqrt(n);blk_cnt=(n-1)/blk_sz+1; for(int i=1;i<=blk_cnt;i++){ L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,n); for(int j=L[i];j<=R[i];j++) bel[j]=i; } for(int i=(inv[0]=inv[1]=1)+1;i<=MAXX;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++){ int tmp=a[i]; for(int j=1;j<=prcnt;j++) if(tmp%pr[j]==0){ int cnt=0; while(tmp%pr[j]==0) tmp/=pr[j],cnt++; if(pr[j]<=LIM) sum[i][j]=cnt; else{ c[i][++cc[i]]=pr[j]; if(cnt==2) c[i][++cc[i]]=pr[j]; } } if(tmp>1) c[i][++cc[i]]=tmp; for(int j=1;j<=cc[i];j++) key[++ccnt]=c[i][j]; } sort(key+1,key+ccnt+1); for(int i=1;i<=n;i++) for(int j=1;j<=prcnt;j++){ if(pr[j]>LIM) break;sum[i][j]+=sum[i-1][j]; } for(int i=1;i<=ccnt;i++) if(key[i]^key[i-1]) uni[++num]=key[i]; for(int i=1;i<=n;i++) for(int j=1;j<=cc[i];j++) c[i][j]=lower_bound(uni+1,uni+num+1,c[i][j])-uni; for(int i=1;i<=qu;i++){ scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;res[i]=1; for(int j=1;j<=prcnt;j++){ if(pr[j]>LIM) break; res[i]=1ll*res[i]*(sum[q[i].r][j]-sum[q[i].l-1][j]+1)%MOD; } } sort(q+1,q+qu+1);int cl=1,cr=0; for(int i=1;i<=qu;i++){ while(cr<q[i].r) ins(++cr); while(cl>q[i].l) ins(--cl); while(cl<q[i].l) del(cl++); while(cr>q[i].r) del(cr--); res[q[i].id]=1ll*res[q[i].id]*mul%MOD; } for(int i=1;i<=qu;i++) printf("%d\n",res[i]); return 0; }