CF1422F Boring Queries 題解
阿新 • • 發佈:2022-02-11
根號分治+ST表+主席樹區間出現過的數的乘積
Statement
給定一個長度為 \(n\) 的序列 \(a\) 以及 \(q\) 次詢問 。
每次詢問包含 \(2\) 個整數 \(l,r\) ,你需要求出區間 \([l,r]\) 的最小公倍數對 \(10^9 + 7\) 取模的結果。
詢問強制線上 。
資料範圍: \(1\leq n,q\leq 10^5,1 \leq a_i\leq 2\cdot 10^5,1\leq x,y \leq 10^5\)
Solution
題目即是求 \(\prod p_i^{c_i}\) ,\(c_i=\max\{a_{x,i}\}\) ,\(a_{x,i}\) 表示 \(a_x\)
看到值域 \(\le 10^5\) ,考慮值域分塊
在 \(\sqrt{10^5}\) 的範圍內只有 \(87\) 個素數,所以我們可以開 \(87\) 個 ST 表解決
然後我們把 \(a[i]\) 中小於 \(\sqrt{10^5}\) 的素因子全部除掉,則 \(a[i]\) 只能是 \(1\) 或者單個 \(>\sqrt{10^5}\) 的素數
問題就變成了算區間出現過的數的乘積,可以參照 HH 的項鍊 中的主席樹做法
設 \(pre_i\) 表示值 \(i\) 前一次出現的位置,詢問 \(l,r\) 即是:\(\prod [pre_i<l]a_i\)
那麼我們搞一個主席樹,\(root[k]\) 儲存 \(pre_i\le k\) 的資訊
發現從 \(root[k-1]\) 到 \(root[k]\) 僅僅可能增加一個位置,所以確實可以主席樹
特別的,\(root[0]\) 記錄每個數第一次出現的位置的資訊,方便詢問
時間複雜度 \(O(能過)\)
Code
注意到 ST 表空間有點卡,開成 \(char\)
#include<bits/stdc++.h> #define ls lc[rt] #define rs rc[rt] #define mid ((l+r)>>1) #define swap(x,y) x^=y^=x^=y using namespace std; typedef long long ll; const int N = 2e5+5; const int K = 450; const int mod = 1e9+7; int read(){ int s=0,w=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();} while(isdigit(ch))s=s*10+(ch^48),ch=getchar(); return s*w; } int a[N],prime[100],lc[N*18],rc[N*18]; int pos[N<<1],b[N],Log[N],rot[N]; char f[90][N][18]; ll t[N*18],pw[100][21]; int n,q,cnt,siz; bool vis[K]; void getprime(int n){ for(int i=2;i<=n;++i){ if(!vis[i])prime[++cnt]=i; for(int j=1;j<=cnt&&i*prime[j]<=n;++j){ vis[i*prime[j]]=true; if(i%prime[j]==0)break; } } } int ask(int l,int r,int k){ int t=Log[r-l+1]; return max(f[k][l][t],f[k][r-(1<<t)+1][t]); } void pushup(int rt){t[rt]=t[ls]*t[rs]%mod;} void build(int l,int r,int &rt){ t[rt=++siz]=1; if(l==r)return t[rt]=1,void(); build(l,mid,ls),build(mid+1,r,rs); } void alter(int l,int r,int rt,int id,int v){ if(l==r)return t[rt]=v,void(); id<=mid?alter(l,mid,ls,id,v):alter(mid+1,r,rs,id,v); pushup(rt); } void insert(int l,int r,int rt,int& nw,int id,int v){ nw=++siz,lc[nw]=ls,rc[nw]=rs; if(l==r)return t[nw]=v,void(); id<=mid?insert(l,mid,ls,lc[nw],id,v):insert(mid+1,r,rs,rc[nw],id,v); pushup(nw); } ll query(int l,int r,int rt,int L,int R){ if(L<=l&&r<=R)return t[rt]; ll res=1; if(L<=mid)res=query(l,mid,ls,L,R); if(mid<R)res=res*query(mid+1,r,rs,L,R)%mod; return res; } signed main(){ n=read(),getprime(K-1); for(int i=1;i<=cnt;++i) for(int j=pw[i][0]=1;j<=20;++j) pw[i][j]=pw[i][j-1]*prime[i]%mod; for(int i=1;i<=n;++i){ a[i]=read(); for(int j=1;j<=cnt;++j) while(a[i]%prime[j]==0) f[j][i][0]++,a[i]/=prime[j]; } q=read(); for(int i=2;i<=n;++i)Log[i]=Log[i>>1]+1; for(int k=1;k<=cnt;++k)for(int i=1;(1<<i)<=n;++i) for(int j=1;j+(1<<i)-1<=n;++j) f[k][j][i]=max(f[k][j][i-1],f[k][j+(1<<(i-1))][i-1]); build(1,n,rot[0]); for(int i=1;i<=n;++i){ if(!pos[a[i]])alter(1,n,rot[0],i,a[i]); else b[pos[a[i]]]=i; pos[a[i]]=i; } for(int i=1;i<=n;++i) if(b[i])insert(1,n,rot[i-1],rot[i],b[i],a[b[i]]); else rot[i]=rot[i-1]; ll lastans=0,l,r; while(q--){ l=(read()+lastans)%n+1,r=(read()+lastans)%n+1,lastans=1; if(l>r)swap(l,r); for(int i=1;i<=cnt;++i) (lastans*=pw[i][ask(l,r,i)])%=mod; lastans=(lastans*query(1,n,rot[l-1],l,r))%mod; printf("%lld\n",lastans); } return 0; } /* 3 2 3 5 4 1 3 3 3 2 3 2 3 */