区间 lcm,强制在线。
考虑和 这题 类似的思路,维护很多质因子是不可能的,所以把小质数先拿出来预处理。
预处理的是区间该质因数的最大次数,所以需要 ST 表,当然也可以线段树但是麻烦。
注意到 ai≤2×105,所以处理 ≤√2×105 的质数即可。
然后每个数至多只剩一个因子 bigi 了,考虑记 prei 表示 bigi 上一次作为最大质因子出现的下标。
则相当于求:r∏i=lai×[prei<l]。
我的做法比较神奇,以下标为主席树的版本,以 prei 为内层的下标。则每次查询相当于 r 版本的前缀积和 l−1 版本的前缀积逆元相乘。
对了,略卡空间。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, B = 450, mod = 1e9 + 7;
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * 1ll * a % mod;
a = a * 1ll * a % mod, k >>= 1;
}
return res;
}
int n, q, a[N];
short lg[N];
struct RMQ {
short st[N][18];
void init() {
for (int i = 1; i <= 17; i++)
for (int j = 1; j <= n; j++) {
if (j + (1 << i) - 1 > n) break;
st[j][i] = max(st[j][i - 1], st[j + (1 << i - 1)][i - 1]);
}
}
inline int query(int l, int r) {
if (l > r) swap(l, r); short len = lg[r - l + 1];
return max(st[l][len], st[r - (1 << len) + 1][len]);
}
} ST[87];
bool st[B]; int pr[87], tt = 0;
int lst[N << 1], pre[N], big[N];
void getprime() {
for (int i = 2; i <= 448; i++) {
if (st[i]) continue;
pr[++tt] = i;
for (int j = i + i; j <= 448; j += i) st[j] = 1;
}
// tt = 1;
for (int i = 1; i <= n; i++) {
int x = a[i];
for (int j = 1; pr[j] <= x && j <= tt; j++) {
int c = 0;
while (x % pr[j] == 0) x /= pr[j], c++;
ST[j].st[i][0] = c;
}
if (x > 1) pre[i] = lst[x], lst[x] = i, big[i] = x;
}
for (int j = 1; j <= tt; j++) ST[j].init();
}
int rt[N], tot = 0;
struct Tree { int ls, rs, mul, inv; } tr[N << 6];
void change(int &u, int l, int r, int x, int dm, int dv) { //mul, inv
tr[++tot] = tr[u], u = tot;
if (!tr[u].mul) tr[u].mul = tr[u].inv = 1;
tr[u].mul = tr[u].mul * 1ll * dm % mod, tr[u].inv = tr[u].inv * 1ll * dv % mod;
if (l == r) return;
int mid = l + r >> 1;
if (x <= mid) change(tr[u].ls, l, mid, x, dm, dv);
else change(tr[u].rs, mid + 1, r, x, dm, dv);
}
int querym(int u, int l, int r, int ql, int qr) {
if (ql > qr || !u) return 1;
if (l >= ql && r <= qr) return tr[u].mul;
int mid = l + r >> 1, res = 1;
if (ql <= mid) res = querym(tr[u].ls, l, mid, ql, qr);
if (qr > mid) res = res * 1ll * querym(tr[u].rs, mid + 1, r, ql, qr) % mod;
return res;
}
int queryv(int u, int l, int r, int ql, int qr) {
if (ql > qr || !u) return 1;
if (l >= ql && r <= qr) return tr[u].inv;
int mid = l + r >> 1, res = 1;
if (ql <= mid) res = queryv(tr[u].ls, l, mid, ql, qr);
if (qr > mid) res = res * 1ll * queryv(tr[u].rs, mid + 1, r, ql, qr) % mod;
return res;
}
inline int query(int l, int r, int k) { return querym(rt[r], 0, n - 1, 0, k - 1) * 1ll * queryv(rt[l - 1], 0, n - 1, 0, k - 1) % mod; }
void init() {
getprime();
for (int i = 1; i <= n; i++) {
rt[i] = rt[i - 1];
if (big[i]) change(rt[i], 0, n - 1, pre[i], big[i], qmi(big[i], mod - 2) );
}
}
int main() {
scanf("%d", &n);
lg[1] = 0; for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;-
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
init();
scanf("%d", &q);
int lst = 0;
while (q--) {
int l, r; scanf("%d%d", &l, &r);
l = (l + lst) % n + 1, r = (r + lst) % n + 1;
if (l > r) swap(l, r);
// cout << '\t' << l << ' ' << r << endl;
int ans = 1;
for (int i = 1; i <= tt; i++) ans = ans * 1ll * qmi(pr[i], ST[i].query(l, r)) % mod;
ans = ( ans * 1ll * query(l, r, l) ) % mod;
printf("%d\n", lst = (ans % mod) );
}
return 0;
}