Codeforces Round #675 (Div. 2) F. Boring Queries 区间lcm + 主席树
傳送門
文章目錄
- 題意:
- 思路:
題意:
給你一個長度為nnn的序列aaa,qqq個詢問,每次詢問[l,r][l,r][l,r]內的lcmlcmlcm是多少,對1e9+71e9+71e9+7取模。
n≤1e5,a≤2e5,q≤1e5n\le1e5,a\le2e5,q\le1e5n≤1e5,a≤2e5,q≤1e5
思路:
關于lcmlcmlcm,對于兩個數的lcmlcmlcm,為了防止爆炸我們通常寫成a/gcd(a,b)?ba/gcd(a,b)*ba/gcd(a,b)?b,如果求1?n1-n1?n所有數的lcmlcmlcm并且要求取模,而且a,ba,ba,b都比較大,我們就不能這么求了,考慮將其分解為質因子的形式。
考慮a?b/gcd(a,b)a*b/gcd(a,b)a?b/gcd(a,b)到底干了個什么事情,其實他將a,ba,ba,b的每個質因子的冪次都取了maxmaxmax之后就得到了他們的lcmlcmlcm,換句話說,原本a=p1x1p2x2...pnxna=p_1^{x1}p_2^{x2}...p_n^{xn}a=p1x1?p2x2?...pnxn?,b=p1y1p2y2...pnynb=p_1^{y1}p_2^{y2}...p_n^{yn}b=p1y1?p2y2?...pnyn?,那么lcm(a,b)=p1max(x1,y1)p2max(x2,y2)...pnmax(xn,yn)lcm(a,b)=p_1^{max(x1,y1)}p_2^{max(x2,y2)}...p_n^{max(x_n,y_n)}lcm(a,b)=p1max(x1,y1)?p2max(x2,y2)?...pnmax(xn?,yn?)?,因為gcd(a,b)gcd(a,b)gcd(a,b)是取了minminmin,除去之后就剩maxmaxmax啦。
所以我們可以通過維護質因子的個數,最后就快速冪一下就好。
如果帶修的話,需要掛到線段樹上,考慮值域ai≤2e5a_i\le 2e5ai?≤2e5的情況,我們可以對于≤500\le 500≤500的質數每個都建一顆線段樹,最多也就909090個左右,讓后對于其他的素數,冪次一定都是111,我們查詢區間內不同的數的乘積,用主席樹維護一下即可,對于909090個線段樹,我們只需要維護區間maxmaxmax即可。
還可以將909090棵線段樹換成ststst表,讓后用charcharchar存,這樣能快點。
這個的復雜度O(n?90?logn)O(n*90*logn)O(n?90?logn),常數有點大,雖然卡卡能過,但是顯然不優,我們考慮更優解法。
考慮我們離線怎么做,顯然我們套路的將其按照右端點排序,現在我們就固定了右端點,左端點在lll,由于lcmlcmlcm是單調不減的,所以考慮一個單調性,我們將其移動到l?1l-1l?1的話貢獻是多少呢?假設al?1a_{l-1}al?1?有一個質因子ppp,其冪次為xxx,在[l,r][l,r][l,r]之間的ppp冪次為yyy,假設此時x>yx>yx>y,那么顯然他對lcmlcmlcm的貢獻為px?yp^{x-y}px?y,否則其沒有貢獻。
這啟發了我們什么呢?我們是否可以像線段樹離線維護數出現的最后位置一樣,維護質因子出現的最后位置呢?假設現在有兩個位置i<ji<ji<j,質因子pix,pjyp_i^{x},p_j^{y}pix?,pjy?,假設x<yx<yx<y,那么將iii位置的質因子都刪去,在jjj位置乘上pyp^ypy顯然是最優的,這樣來看直接取最后的位置是沒問題的,但是如果x>yx>yx>y,這個時候按照上面說的我們就不會更新,但是如果后面查詢[j,pos][j,pos][j,pos]的時候,由于我們沒加上jjj位置質因子的貢獻,所以會導致答案錯誤。
所以我們考慮維護一下每個質因子的每個冪次出現的位置,這樣對于x>yx>yx>y的情況,相當于將iii位置除上個pyp_ypy?,變成px?yp^{x-y}px?y,讓后將jjj位置更新為pyp^ypy,這樣查詢[j,pos][j,pos][j,pos]的時候不會丟掉jjj的貢獻,并且查詢[x,y][x,y][x,y]的時候,由于維護的是乘積,即px?y?py=pxp^{x-y}*p^{y}=p^xpx?y?py=px,也不會丟掉iii位置的最大值,這樣這個問題就完美解決了。
復雜度由aaa來確定,由于每個數最多有不超過logalogaloga個質因子,所以時間復雜度上界是O(nlog2n)O(nlog^2n)O(nlog2n),空間復雜度O(nlog2n)O(nlog^2n)O(nlog2n)。
// Problem: F. Boring Queries // Contest: Codeforces - Codeforces Round #675 (Div. 2) // URL: https://codeforces.com/contest/1422/problem/F // Memory Limit: 512 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org)//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native") //#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #include<ctime> #include<cstdlib> #include<random> #include<cassert> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define pb push_back #define mk make_pair #define Mid ((tr[u].l+tr[u].r)>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define random(a,b) ((a)+rand()%((b)-(a)+1)) #define db puts("---") using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); } //void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); } //void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII;const int N=200010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6;int n,m; int a[N],pre[N]; int root[N],idx; struct Node {int l,r;LL mul; }tr[N*100]; int prime[2*N+10],cnt,ne[2*N]; bool st[2*N+10];void get_prime(int n) {for(int i=2;i<=n;i++){if(!st[i]) prime[cnt++]=i,ne[i]=i;for(int j=0;prime[j]<=n/i;j++){st[prime[j]*i]=true;ne[prime[j]*i]=prime[j];if(i%prime[j]==0) break; } } } LL qmi(LL a,LL b) {LL ans=1;while(b) {if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans%mod; }void build(int &u,int l,int r) {u=++idx; tr[u].mul=1;if(l==r) return;int mid=(l+r)>>1;build(tr[u].l,l,mid); build(tr[u].r,mid+1,r); }void insert(int p,int &q,int l,int r,int pos,int val) {q=++idx; tr[q]=tr[p];tr[q].mul*=val; tr[q].mul%=mod;if(l==r) return;int mid=(l+r)>>1;if(pos<=mid) insert(tr[p].l,tr[q].l,l,mid,pos,val);else insert(tr[p].r,tr[q].r,mid+1,r,pos,val); }LL query(int u,int l,int r,int ql,int qr) {if(!u) return 1;if(l>=ql&&r<=qr) return tr[u].mul;LL ans=1,mid=(l+r)>>1;if(ql<=mid) ans=ans*query(tr[u].l,l,mid,ql,qr)%mod;if(qr>mid) ans=ans*query(tr[u].r,mid+1,r,ql,qr)%mod;return ans; }int main() { // ios::sync_with_stdio(false); // cin.tie(0);get_prime(3e5);scanf("%d",&n);build(root[0],1,n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=n;i++) {int now=a[i];root[i]=root[i-1];while(now!=1) {int f=ne[now];int inv=qmi(ne[now],mod-2);vector<PII>v;LL fun=1;while(now%f==0) {fun*=f; now/=f;if(pre[fun]) v.pb({pre[fun],inv});pre[fun]=i;}insert(root[i],root[i],1,n,i,fun);LL all=1;for(int j=0;j<v.size();j++) {all*=v[j].Y,all%=mod;if(j==v.size()-1||v[j].X!=v[j+1].X) insert(root[i],root[i],1,n,v[j].X,all),all=1;}}}LL last=0;scanf("%d",&m);while(m--) {int l,r; scanf("%d%d",&l,&r);l=(1ll*l+last)%n+1; r=(1ll*r+last)%n+1;if(l>r) swap(l,r);printf("%lld\n",last=query(root[r],1,n,l,r));} return 0; } /**/總結
以上是生活随笔為你收集整理的Codeforces Round #675 (Div. 2) F. Boring Queries 区间lcm + 主席树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XXI Open Cup. Grand
- 下一篇: 《人件(原书第3版)》—— 01 此时