📜  门|门CS 2008 |第 49 题(1)

📅  最后修改于: 2023-12-03 15:42:22.226000             🧑  作者: Mango

门门CS 2008 第 49 题

题目描述

给定一个长度为 $N$ 的序列 $a$,现在对于各个 $i(1\leq i\leq N)$,需要求出一个范围 $[L,R]$,满足区间 $[L,R]$ 中所有的元素都与 $a_i$ 的质因子集合相同,并且 $i$ 不在范围 $[L,R]$ 中。如果存在多个合法的答案,那么我们需要找到长度最短的那个答案。如果不存在这样的范围,那么说明 $a_i$ 的质因子互不相同。

解题思路

本题是一道比较有难度的数学 + 数据结构题目。

首先,我们需要快速求出一个数的所有质因子。可以用线性筛法预处理出每个数的最小质因子,然后利用此信息,即可对一个数进行分解质因数。

接下来,考虑对于一个 $i$,我们如何快速求出其所对应的答案区间。我们定义一个数 $x$ 的质因子集合为其所有质因子的集合,方便起见,如果 $x$ 是一个质数,那么其质因子集合就是 ${x}$。

首先我们考虑 $i$ 自成区间时的情况,即需要求出在 $[1,i-1]$ 和 $[i+1,N]$ 这两个区间中,第一个质因子集合与 $a_i$ 相同的数的位置。为此,我们可以用两个 $map$ 分别存储这两个区间中,每种质因子集合最后一次出现的位置,然后对于每种质因子集合,求出满足质因子集合相同的最远两个位置即可。

然后再考虑 $i$ 不自成区间的情况。假设存在一个区间 $[L,R]$,使得 $i\in[L,R]$,且区间 $[L,R]$ 中所有的元素都与 $a_i$ 的质因子集合相同。那么显然必须满足 $a_L,\cdots,a_{i-1}$ 的最小质因子集合与 $a_i$ 相同,同时 $a_{i+1},\cdots,a_R$ 的最小质因子集合也与 $a_i$ 相同。因此,我们可以用两个 $vector$ 分别维护 $a_1,\cdots,a_{i-1}$ 中最小质因子集合相同的位置和 $a_{i+1},\cdots,a_N$ 中最小质因子集合相同的位置,然后枚举 $[L,i-1]$ 和 $[i+1,R]$,从而求出满足 $i\in[L,R]$ 的最短区间。

代码实现
// C++14

#include <bits/stdc++.h>
using namespace std;

constexpr int MAXN = 100005, MAXP = 1000005;

int n, a[MAXN], minp[MAXP];
bitset<MAXN> vis;
unordered_map<int, int> mp1, mp2;
vector<int> vec1[MAXP], vec2[MAXP];

void prework() {
    for (int i = 2; i < MAXP; ++i) {
        if (!minp[i]) minp[i] = i;
        for (int j = 0; j * i < MAXP; ++j)
            if (!minp[i * j]) minp[i * j] = i;
    }
}

void solve(int i, int& L, int& R) {
    mp1.clear(), mp2.clear();
    for (int j = 1; j < i; ++j)
        if (!vis[j]) mp1[minp[a[j]]] = j;
    for (int j = i + 1; j <= n; ++j)
        if (!vis[j]) mp2[minp[a[j]]] = j;
    L = R = n + 1;
    for (auto& [p, j] : mp1) {
        if (!mp2.count(p)) continue;
        int k = mp2[p];
        L = min(L, j), R = min(R, k);
    }
    for (auto& [p, j] : mp2) {
        if (!mp1.count(p)) continue;
        int k = mp1[p];
        L = min(L, k), R = min(R, j);
    }
}

int main() {
    prework();
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", a + i);
        int x = a[i];
        while (x > 1) vec1[minp[x]].emplace_back(i), x /= minp[x];
    }
    for (int i = n; i >= 1; --i) {
        int x = a[i];
        while (x > 1) vec2[minp[x]].emplace_back(i), x /= minp[x];
    }
    int ans = n + 1;
    for (int i = 1; i <= n; ++i) {
        vis.set(i);
        int L, R;
        solve(i, L, R);
        if (L <= n && R <= n) ans = min(ans, R - L + 1);
        for (int x : vec1[minp[a[i]]]) vis.set(x);
        for (int x : vec2[minp[a[i]]]) vis.set(x);
    }
    printf("%d\n", ans > n ? -1 : ans);
    return 0;
}