「Boring Queries」Solution
题意简述
给定一个长度为
n
n
n 的序列
a
a
a 以及
q
q
q 次询问 。
每次询问包含
2
2
2 个整数
l
,
r
l,r
l,r ,你需要求出区间
[
l
,
r
]
[l,r]
[l,r] 的最小公倍数对
1
0
9
+
7
10^9 + 7
109+7 取模的结果。
询问强制在线 。对于每次读入的
x
,
y
x,y
x,y ,你需要进行以下操作得到
l
,
r
l,r
l,r :
l
=
(
x
+
l
a
s
t
)
m
o
d
n
+
1
,
r
=
(
y
+
l
a
s
t
)
m
o
d
n
+
1
l=(x+last)\ mod\ n+1,r=(y+last)\ mod\ n+1
l=(x+last) mod n+1,r=(y+last) mod n+1,
l
a
s
t
last
last 为上次查询的答案 。
特别地,如果
l
l
l 比
r
r
r 大,交换
l
,
r
l,r
l,r。
- 1 ≤ n , q , x , y ≤ 1 0 5 , 1 ≤ a i ≤ 2 × 1 0 5 1\leq n,q,x,y\leq 10^5,1 \leq a_i\leq 2\times10^5 1≤n,q,x,y≤105,1≤ai≤2×105
思路
看到最小公倍数,首先想到的应该是唯一分解定理。
我们知道,一个数
a
i
a_i
ai 可被唯一表达为:
a i = ∏ j = 1 n p j k i , p j ( p 1 < p 2 . . . < p n − 1 < p n ) a_i=\prod_{j=1}^{n} p_j^{k_{i,p_j}}(p_1 < p_2 ... < p_{n-1} < p_n) ai=j=1∏npjki,pj(p1<p2...<pn−1<pn)
其中
p
j
p_j
pj 质数,
k
i
,
j
,
≥
1
k_{i,j,} \ge 1
ki,j,≥1,
n
n
n 即
a
i
a_i
ai 的质因子数量。
而一个区间
[
l
,
r
]
[l , r]
[l,r] 的
l
c
m
lcm
lcm 也可被唯一表达为:
l c m = ∏ j = 1 n p j m a x { k l , p j , k l + 1 , p j , . . . k r , p j } lcm=\prod_{j=1}^n p_j^{max\{k_{l,p_j},k_{l+1,p_j},...k_{r,p_j} \}} lcm=j=1∏npjmax{kl,pj,kl+1,pj,...kr,pj}
因此,我们求 [ l , r ] [l,r] [l,r] 区间的最小公倍数,等价于求每一个质因子在这个区间里的每个数中,最多出现了多少次。
考虑 根号分治,我们平时求一个数的因子的时候,往往只会枚举到 n \sqrt{n} n,而不会枚举到 n n n,这是因为如果一个质因数 x > n x > \sqrt{n} x>n,那么它在 n n n 中最多出现一次,即 k x = 1 k_x=1 kx=1。那么对于这类数来说,它对答案的贡献,只有 有或没有 的区别,对于这类数我们单独维护。
-
对于 x ≤ n x \le \sqrt{n} x≤n,注意到值域只有 200000 200000 200000,所以这类数不会太多,事实上,只有 86 86 86 个。所以我们只需要建 86 86 86 个 ST \text{ST} ST 表,然后暴力维护这类数在区间 [ l , r ] [l,r] [l,r] 出现的最大次数即可。
-
对于 x > n x > \sqrt{n} x>n,如果我们直接将 [ l , r ] [l,r] [l,r] 中这类数累乘起来,很显然会算重复。因为如果某个质因子 x x x 在 [ l , r ] [l,r] [l,r] 出现的次数 k x ≥ 1 k_x \ge 1 kx≥1,那么它对答案的贡献只有 x x x,而非 x k x x^{k_x} xkx。
对于这类某个数在区间内贡献次数 ≤ 1 \le 1 ≤1 的情况,有一个常见 trick \text{trick} trick, 即只考虑这个数在区间内第一次出现的位置,不妨令 l s t x lst_x lstx 为上一个 x x x 的位置,那么 x x x 在 [ l , r ] [l,r] [l,r] 第一次出现就等价于 l s t x ∈ [ 0 , l − 1 ] lst_x \in [0,l-1] lstx∈[0,l−1]。
那么我们对于一个数 a i , i ∈ [ l , r ] a_i,i \in [l,r] ai,i∈[l,r],如果 a i a_i ai 的某个质因数 x x x 满足 l s t x ∈ [ 0 , l − 1 ] lst_x \in [0,l-1] lstx∈[0,l−1],那么这个 x x x 便对 [ l , r ] [l,r] [l,r] 的 l c m lcm lcm 产生贡献,即 l c m ← l c m × x lcm \leftarrow lcm \times x lcm←lcm×x。
这很显然是一个特定区间内值域段的查询,用主席树维护即可。
总时间复杂度为
O
(
q
×
l
o
g
(
V
)
+
(
n
+
q
)
×
V
)
O(q \times log(V) + (n+q) \times \sqrt{V})
O(q×log(V)+(n+q)×V),然而时限只有
3
s
e
c
3 sec
3sec,所以需要卡卡常,虽然
CF
\text{CF}
CF 是神机 。
代码
具体实现可以看代码。
#include <bits/stdc++.h>
#define siz sqrt(200000)
#define ll long long
using namespace std;
const int MAXN = 1e5 + 5 , MOD = 1e9 + 7;
namespace IO{
#define il inline
#define Re register
char B[1 << 20], *S = B, *T = B , obuf[1 << 20] , *p3 = obuf;
#define getchar() (S == T && (T = (S = B) + fread(B, 1, 1 << 15, stdin), S == T) ? EOF : *S++)
#define putchar(x) (p3-obuf<1 << 15)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
il void read(Re item &x) {
char c(getchar());
x = 0;
int f(1);
while (c < '0' || c > '9') {if (c == '-') f = -1;c = getchar();}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
x *= f;
}
template<typename item, typename ...Arg>
il void read(item &tmp, Arg& ...tmps) {read(tmp);read(tmps...);}
template<typename item>
il void write(Re item x) {
if (x < 0) {putchar('-') , x = -x;}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
int n , q , a[MAXN] , idx , rk[MAXN << 1] , lst[MAXN << 1];
short Max[90][MAXN][18];
ll lastans;
bitset<200005> vis;
vector<int> small;
ll qpow(ll base , int k) {
ll res = 1;
while(k) {
if (k & 1) res = res * base % MOD;
base = base * base % MOD;
k >>= 1;
}
return res;
}
ll inv(ll x) {return qpow(x , MOD - 2);}
namespace Segement{
static int tot , rt[MAXN];
struct tree{
int l , r , ls , rs;
ll mul;
tree(){mul = 1;}
}tree[MAXN << 5];
inline int build(int p , int l , int r) {
p = ++ tot;
tree[p].l = l , tree[p].r = r;
if (l == r) return p;
int mid = l + r >> 1;
tree[p].ls = build(p , l , mid) , tree[p].rs = build(p , mid + 1 , r);
return p;
}
int newnode(int p) {
tree[++ tot] = tree[p];
return tot;
}
inline int update(int p , int x , ll v) {
int now = newnode(p);
tree[now].mul = tree[now].mul * v % MOD;
if (tree[now].l == tree[now].r) return now;
int mid = tree[now].l + tree[p].r >> 1;
if (x <= mid) tree[now].ls = update(tree[now].ls , x , v);
else tree[now].rs = update(tree[now].rs , x , v);
return now;
}
inline ll query(int p , int p2 , int l , int r) {
if (tree[p].l >= l && tree[p].r <= r) return tree[p2].mul * inv(tree[p].mul) % MOD;
int mid = tree[p].l + tree[p].r >> 1;
ll mul = 1;
if (l <= mid) mul = query(tree[p].ls , tree[p2].ls , l , r);
if (r > mid) mul = (mul * query(tree[p].rs , tree[p2].rs , l , r)) % MOD;
return mul;
}
}
using namespace Segement;
int GetMax(int pos , int l , int r) {
int s = log2(r - l + 1);
return max(Max[pos][l][s] , Max[pos][r - (1 << s) + 1][s]);
}
int main() {
read(n);
for (int i = 2 ; i <= 200000 ; i ++) {
if (vis[i]) continue;
rk[i] = ++ idx;
if (i <= siz) small.push_back(i);
for (int j = i ; j <= 200000 ; j += i) vis[j] = 1;
}
rt[0] = build(1 , 0 , 2e5);
for (int i = 1 , x ; i <= n ; i ++) {
rt[i] = rt[i - 1];
read(x);
for (int j = 2 ; j * j <= x ; j ++) {
int cnt = 0;
while(x % j == 0) x /= j , cnt ++;
Max[rk[j]][i][0] = cnt;
}
if (x > siz) {
rt[i] = update(rt[i] , lst[x] , x) , lst[x] = i;
} else Max[rk[x]][i][0] = 1;
}
for (int pos = 1 ; pos <= 86 ; pos ++) {
for (int j = 1 ; (1 << j) <= n ; j ++) {
for (int i = 1 ; i + (1 << j) - 1 <= n ; i ++) {
Max[pos][i][j] = max(Max[pos][i][j - 1] , Max[pos][i + (1 << j - 1)][j - 1]);
}
}
}
read(q);
while(q --) {
int l , r;
ll ans = 1;
read(l , r);
l = (l + lastans % n) % n + 1 , r = (r + lastans % n) % n + 1;
if (l > r) swap(l , r);
for (int v : small) {
int pi = GetMax(rk[v] , l , r);
ans = ans * qpow(v , pi) % MOD;
}
ans = ans * query(rt[l - 1] , rt[r] , 0 , l - 1) % MOD;
write(lastans = ans) , putchar('\n');
}
fwrite(obuf , p3 - obuf , 1 , stdout);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】