传送门
文章目录
- 题意:
- 思路:
题意:
给你一个长度为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=p1x1p2x2...pnxn,b=p1y1p2y2...pnynb=p_1^{y1}p_2^{y2}...p_n^{yn}b=p1y1p2y2...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;
}
/**/