「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 1n,q,x,y105,1ai2×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=1npjki,pj(p1<p2...<pn1<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=1npjmax{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} xn ,注意到值域只有 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 kx1,那么它对答案的贡献只有 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,l1]
    那么我们对于一个数 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,l1],那么这个 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 lcmlcm×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;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值