CF1516D Cut 题解

描述:

给定 \(n\) 个数的序列和 \(q\) 次询问,每次询问给定区间 \([l,r]\) ,求出至少将其分割成几个子区间,才使得每个子区间的 \(\operatorname{lcm}\) 等于区间内所有数的乘积。

思路:

区间 \([l,r]\) 内所有数的乘积等于其 \(\operatorname{lcm}\) 当且仅当 \(\gcd(a_i,a_j)=1\) ,其中 \(i\neq j,\; i,j \in [l,r]\) 。

那么可以对于每次给出的区间 \([l,r]\) 从左到右暴力扫一遍,过不去,考虑优化。

可以预处理每个左端点 \(i\) 所对应的最靠右的右端点 \(nex(i)\) 。

从左到右枚举 \(i\) ,发现 \(nex(i) \ge nex(i-1)\) ,因为少了 \(1\) 个数,右端点必定不会更近。

那么就可以 \(O(n)\) 双指针预处理 \(nex(i)\) 。

对于如何判断两两互质,可以用线性筛预处理出 \(10^5\) 以内的质数,再枚举每个质数 \(j\) ,对于其倍数 \(i\) ,\(i \le 10^5\) ,将 \(j\) 加入 \(i\) 的 质因数集合,换个顺序,就可以从 \(O(n\sqrt{n})\) 变成 \(O(n\log n)\) 。

如果从 \(l\) 开始,往右边跳的话,如果它们都互相不互质,单次就变成 \(O(n)\) 了。

容易想到,可以用倍增优化,单次就变成 \(O(\log n)\) 。

总时间复杂度 \(O(n)+O(n\log n)+O(n\log n)=O(n\log n)\) 。

\(\mathtt{Ps:}\)

\(nex(i)\) 表示的不是以 \(i\) 为左端点的最靠右的右端点,而是下一个区间的左端点。

预处理 \(st(i,j)\) 时,必须要加上 \(st(k,n+1)\) 为 \(n+1\) ,否则其值为 \(0\) ,可能会一直跳到 \(2^m\) 。

代码:

Prime 预处理质数

Calc 判断当前的 \(a_j\) 能否加入

Del 删除 \(a_{i-1}\)

Ask 倍增查询答案,注意最后一定要加 \(1\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>

#define LL long long
#define PR pair<LL,int>

using namespace std;

const int kN = 1e5;

int n, a[kN + 10], q;
int prime[kN + 10], tot;
int nex[kN + 10], st[30][kN + 10];
int _2[30], num[kN + 10];

vector<int> e[kN + 10];

bool isp[kN + 10];

void Prime() {
    isp[1] = 1;

    for (int i = 2; i <= kN; ++i) {
        if (!isp[i]) {
            prime[++tot] = i;
        }

        for (int j = 1; j <= tot && prime[j]*i <= kN; ++j) {
            isp[i * prime[j]] = 1;

            if (i % prime[j] == 0) {
                break;
            }
        }
    }

    for (int i = 1; i <= tot; ++i) {
        for (int j = prime[i]; j <= kN; j += prime[i]) {
            e[j].push_back(i);
        }
    }
}

bool Calc(int x) {
    for (int i = 0; i < e[x].size(); ++i) {
        int k = e[x][i];

        if (num[k]) {
            return 0;
        }
    }

    for (int i = 0; i < e[x].size(); ++i) {
        int k = e[x][i];
        num[k] = 1;
    }

    return 1;
}

void Del(int x) {
    if (!x) {
        return;
    }

    for (int i = 0; i < e[x].size(); ++i) {
        int k = e[x][i];
        num[k] = 0;
    }
}

int Ask(int l, int r) {
    int cnt = 0;

    for (int i = 20; i >= 0; --i) {
        if (st[i][l] <= r) {
            l = st[i][l];
            cnt += _2[i];
        }
    }

    return cnt + 1;
}

int main() {
    scanf("%d%d", &n, &q);

    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }

    Prime();
    _2[0] = 1;

    for (int i = 1; i <= 20; ++i) {
        _2[i] = _2[i - 1] * 2;
    }

    for (int i = 1, j = 0; i <= n; ++i) {
        Del(a[i - 1]);

        while (j < n && Calc(a[j + 1])) {
            j++;
        }

        nex[i] = j + 1;
        st[0][i] = nex[i];
    }

    st[0][n + 1] = n + 1;

    for (int i = 1; i <= 20; ++i) {
        st[i][n + 1] = n + 1;

        for (int j = 1; j <= n; ++j) {
            st[i][j] = st[i - 1][st[i - 1][j]];
        }
    }

    while (q--) {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", Ask(l, r));
    }

    return 0;
}
上一篇:[cf700E]Cool Slogans


下一篇:鸣人追大蛇