cfF. Boring Queries

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cfF. Boring Queries相关的知识,希望对你有一定的参考价值。

cfF. Boring Queries

题意:

n个数组a[],q个询问,每次询问区间[l,r]的lcm值
题目要求强制在线
1<=n<=1e5
1<=a<=2e5
1<=q<=1e5

题解:

添加链接描述
添加链接描述
添加链接描述

我们一般求lcm都是直接通过ab/gcd(a,b)来做,但是现在要对所有lcm取模,且a,b都比较大,就不能直接通过这个计算了,我们开始挖掘lcm更深层的内涵。
a
b/gcd(a,b)的作用其实是对a,b的每个质因子的幂次都取了max,就是说,如果 a = p 1 x 1 ∗ p 2 x 2 . . . . . ∗ p n x n a=p_{1}^{x1}*p_{2}^{x2}.....*p_{n}^{xn} a=p1x1p2x2.....pnxn, b = p 1 y 1 ∗ p 2 y 2 . . . . . ∗ p n y n b=p_{1}^{y1}*p_{2}^{y2}.....*p_{n}^{yn} b=p1y1p2y2.....pnyn,那么 l c m ( a , b ) = p 1 m a x ( x 1 , y 1 ) ∗ p 2 m a x ( x 2 , y 2 ) . . . . . ∗ p n m a x ( x n , y n ) lcm(a,b)=p_{1}^{max(x1,y1)}*p_{2}^{max(x2,y2)}.....*p_{n}^{max(xn,yn)} lcm(a,b)=p1max(x1,y1)p2max(x2,y2).....pnmax(xn,yn)
也就是我们可以通过维护质因子的个数,来求lcm
现在我们开始考虑质因子的个数,因为ai<=2e5,所以对于质因子p,如果 p 2 p^2 p2>2e5,那么它出现的次数只有0/1,那查询一个区间内除重后的这些数的乘积。为了减少空间开支,我们可以用主席树来维护这这些质因子,但是一个区间内有可能出现多个相同质因子,该如何处理?
我们参考P1972 [SDOI2009]HH的项链中主席树的操作,对于右端点为r的区间[…,r],相同数字,我们只维护最靠近r的(对每个因子维护一个最后出现的乘积,这样就不会重复计算)。

如果 p 2 p^2 p2<2e5,最多只有86个质数,那么可以用87个线段树来维护区间max,因为本题并没有涉及修改,rmq问题可以用st表来实现,维护86个RMQ表
复杂度是 O ( 86 ∗ n l o g n ) O(86*nlogn) O(86nlogn)

个人思考:
我感觉分块不能做,多个数的lcm不是所有数的乘积除以所有数的gcd,就比如4 ,8,3
O ( 86 ∗ n l o g n ) O(86*nlogn) O(86nlogn)常数偏大,但是能过,还有 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的做法,之后更新

代码:

#include <bits/stdc++.h>

using namespace std;

const int N= 1e5 + 10, M= 2e5 + 10, mod= 1e9 + 7;

int root[N], ls[N * 40], rs[N * 40], sum[N * 40], num;

int prime[N], Log[N], inv[M], pre[M], fac[M], a[N], cnt, n, m;

vector<int> p[87];

bool st[M];

struct RMQ
{
    char f[N][18];

    void init()
    {
        for (int j= 1; j < 18; j++) {
            for (int i= 1; i + (1 << j) - 1 <= n; i++) {
                f[i][j]= max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
            }
        }
    }

    int query(int l, int r)
    {
        int s= Log[r - l + 1];
        return max(f[l][s], f[r - (1 << s) + 1][s]);
    }
} rmq[87];

void init()
{
    inv[1]= 1;
    for (int i= 2; i < M; i++) {
        inv[i]= 1ll * (mod - mod / i) * inv[mod % i] % mod;
        if (!st[i]) {
            prime[++cnt]= i;
        }
        for (int j= 1; j <= cnt && 1ll * i * prime[j] < M; j++) {
            st[i * prime[j]]= 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
    for (int i= 2; i < N; i++) {
        Log[i]= Log[i / 2] + 1;
    }

    for (int j= 1; prime[j] * prime[j] < M; j++) {
        for (int i= 1; i <= n; i++) {
            while (a[i] % prime[j] == 0) {
                a[i]/= prime[j];
                rmq[j].f[i][0]++;
            }
        }
        rmq[j].init();
        for (int cur= 1; cur < M; cur*= prime[j]) {
            p[j].push_back(cur); //第j个质数所对应的各种次幂
        }
    }

    for (int i= 87; i <= cnt; i++) {
        for (int j= prime[i]; j < M; j+= prime[i]) {
            fac[j]= prime[i];
        }
    }
}

void build(int& rt, int l, int r)
{
    rt= ++num;
    if (l == r) {
        sum[rt]= 1;
        return;
    }
    int mid= l + r >> 1;
    build(ls[rt], l, mid);
    build(rs[rt], mid + 1, r);
    sum[rt]= sum[ls[rt]] * sum[rs[rt]];
}

void update(int& rt, int pre, int l, int r, int x, int v)
{
    rt= ++num;
    ls[rt]= ls[pre];
    rs[rt]= rs[pre];
    sum[rt]= 1ll * sum[pre] * v % mod;
    if (l == r) {
        return;
    }
    int mid= l + r >> 1;
    if (x <= mid) {
        update(ls[rt], ls[pre], l, mid, x, v);
    }
    else {
        update(rs[rt], rs[pre], mid + 1, r, x, v);
    }
}

int query(int rt, int l, int r, int L, int R)
{
    if (l >= L && r <= R) {
        return sum[rt];
    }
    int ans= 1, mid= l + r >> 1;
    if (L <= mid) {
        ans= 1ll * ans * query(ls[rt], l, mid, L, R) % mod;
    }
    if (R > mid) {
        ans= 1ll * ans * query(rs[rt], mid + 1, r, L, R) % mod;
    }
    return ans;
}

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &n);
    for (int i= 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    init();
    build(root[0], 1, n);
    for (int i= 1; i <= n; i++) {
        root[i]= root[i - 1];
        if (!fac[a[i]]) {
            continue;
        }
        update(root[i], root[i], 1, n, i, fac[a[i]]);
        if (pre[fac[a[i]]]) {
            update(root[i], root[i], 1, n, pre[fac[a[i]]], inv[fac[a[i]]]); //将上个位置的fac[a[i]]去掉
        }
        pre[fac[a[i]]]= i;
    }
    scanf("%d", &m);
    int ans= 0;
    for (int i= 1, l, r; i <= m; i++) {
        scanf("%d %d", &l, &r);
        l= (ans + l) % n + 1, r= (ans + r) % n + 1;
        if (l > r) {
            swap(l, r);
        }
        ans= 1;
        for (int j= 1; j <= 86; j++) {
            ans= 1ll * ans * p[j][rmq[j].query(l, r)] % mod;
        }
        ans= 1ll * ans * query(root[r], 1, n, l, r) % mod;
        printf("%d\\n", ans);
    }
    return 0;
}

以上是关于cfF. Boring Queries的主要内容,如果未能解决你的问题,请参考以下文章

Boring Class cdq分治

UVA - 1608 Non-boring sequences(分治法)

[HDU3518]Boring counting(后缀数组)

在哪里可以找到 OTF 字体中的所有 CFF 字体变体集

●UVA 1608 Non-boring sequences

以编程方式将 CFF 字体转换为 OpenType 字体