T1. 最小公倍数

我们称一个正整数是合法的,当且仅当不存在一个非空子序列的 \(\text{lcm}\) 是这个数。题目要求的就是最小的合法的数。

对于两个集合 \(S,T\),我们有 \(\text{lcm}(S \cup T) = \text{lcm}(\text{lcm}(S), \text{lcm}(T))\)。这意味着,对于正整数 \(a, b\),如果存在两个子序列的 \(\text{lcm}\) 分别为 \(a, b\),那么这两个子序列的并集的 \(\text{lcm}\) 会是 \(\text{lcm}(a, b)\) 。换言之,如果 \(a, b\) 合法,那么 \(\text{lcm}(a, b)\) 合法。

考虑其逆否命题:对于正整数 \(a, b\),如果 \(\text{lcm}(a, b)\) 不合法,那么 \(a, b\) 中至少有一个不合法。这意味着,如果 \(x\) 是最小的合法的数,那么 \(x\) 不可以被写成两个数的 \(\text{lcm}\)

这意味着 \(x\) 一定只有一个质因子,即一定是某个质数的幂。由于质数的幂不能被写成两个数的 \(\text{lcm}\),所以如果某个质数的幂不合法,那么它必须包含在 \({a_i}\) 中。

所以换言之,我们需要找的是最小的不在 \(\{a_i\}\) 中的质数的幂。

由于 \(\{a_i\}\) 中有 \(n\) 个数,所以显然答案的上界是第 \(n+1\) 小的质数,令其为 \(L\) 。我们可以首先筛出 \(\leqslant L\) 的质数,然后计算出所有 \(\leqslant L\) 的质数的幂。接下来把它们从小到大排序后,依次查询每一个数是否在原序列中出现过。第一个没出现过的数就是答案。

总复杂度为:\(\mathcal{O}(n\log n)\) (set或二分查找)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

// linear sieve
vector<bool> isp;
vector<int> ps, pf;
void sieve(int mx) {
    isp.resize(mx+1);
    pf.resize(mx+1);
    rep(i, mx+1) pf[i] = i;
    for (int i = 2; i <= mx; ++i) {
        if (pf[i] == i) isp[i] = true, ps.push_back(i);
        rep(j, ps.size()) {
            int x = ps[j]*i;
            if (x > mx) break;
            pf[x] = ps[j];
            if (i%ps[j] == 0) break;
        }
    }
}

void solve() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    set<int> st(a.begin(), a.end());
    
    if (*st.begin() != 1) {
        puts("1");
        return;
    }
    
    const int INF = 2e6;
    
    sieve(INF);
    
    int ans = INF;
    for (int p : ps) {
        if (p > ans) break;
        int q = 1;
        while (1ll*q*p < INF) {
            q *= p;
            if (st.count(q)) continue;
            ans = min(ans, q);
            break;
        }
    }
    
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

T2. 三角

下文中,所有的三角形均代表 未退化 的三角形。

对于整数 \(p \leqslant q\)\((p, q, x)\) 能够形成一个三角形当且仅当 \(q-p < x < q+p\),即 \(x \in (q-p, q+p)\)

假设我们有一个升序数组 \(\{b_i\}\),那么可以观察到,对于 \(i < j\),区间 \((b_j-b_i, b_j+b_i)\) 一定被区间 \((b_j-b_{j-1}, b_j+b_{j+1})\) 包含,这意味着我们只需要考虑排序后相邻的元素。

这样的话,对于一个静态数组,我们可以计算出 \(O(n)\) 个区间,它们的并集大小就是合法的 \(x\) 的数量。

现在我们需要对一个数组的所有前缀分别求解。我们可以考虑逐个添加元素,维护一个排好序的前缀,然后动态地维护区间并。假设当前我们已经处理完了 \([a_1, a_2, \cdots, a_p]\),现在我们要插入 \(a_{p+1}\),设 \(lb\) 为小于 \(a_{p+1}\) 的最大的数,\(rb\) 为大于 \(a_{p+1}\) 的最小的数,那么 \((a_{p+1}-lb, a_{p+1}+lb), (rb-a_{p+1}, rb+a_{p+1})\) 是我们要插入的区间。

总的区间数仍然是 \(\mathcal{O}(n)\) 级别的。我们可以用 \(\text{set}\)/权值线段树等方式维护。

总复杂度为 \(\mathcal{O}(n\log n)\)