可划分数组

可划分数组

1. 基本定义

对数组中的每个位置 i:

  • L[i]:左边最近的与 a[i] 不互质的位置,若不存在则为 0。
  • R[i]:右边最近的与 a[i] 不互质的位置,若不存在则为 n+1。

两个数不互质 ⇔ gcd(a[i], a[j]) > 1。


2. 合法区间的判定(核心)

对于一个区间 [l, r],它是合法的,当且仅当区间内每一个位置 x 都能在区间内找到另一个位置 y(y ≠ x)使得 gcd(a[x], a[y]) > 1。

用 L[x] 和 R[x] 表示,位置 x 在区间 [l, r] 内找不到伙伴,当且仅当:

  • L[x] < l(左边最近的不互质位置在区间外)
  • 且 R[x] > r(右边最近的不互质位置也在区间外)

因此,区间 [l, r] 合法 ⇔ 不存在 位置 x ∈ [l, r] 同时满足 L[x] < l 和 R[x] > r。

等价于:对任意 x ∈ [l, r],若 R[x] > r,则必须 L[x] ≥ l。


3. 固定右端点 r 的扫描思路

我们固定右端点 r,希望找到所有合法的左端点 l(1 ≤ l ≤ r-1,因为区间长度至少为 2)。

定义:

  • mxleft = 所有满足 x ∈ [l, r] 且 R[x] > r 的位置 x 的 L[x] 的最小值。

则区间 [l, r] 合法 ⇔ mxleft ≥ l。


4. DP 转移

令 dp[i] 表示前 i 个元素能划分出的最大段数,dp[0] = 0,其它初始化为 -∞。

对于每个右端点 r(从 2 到 n),我们向左枚举左端点 l(从 r-1 到 1),并维护 mxleft 的值:

  • 初始化 mxleft = L[r](只考虑位置 r 自己)。
  • 当我们将左边界从 l+1 扩展到 l 时,新加入的位置是 l,如果 R[l] > r,则 l 的右侧没有伙伴,需要更新 mxleft = min(mxleft, L[l])。
  • 此时 mxleft 正好是区间 [l, r] 中所有右侧无伙伴位置的 L 最小值。
  • 若 mxleft ≥ l,则区间 [l, r] 合法,此时可用 dp[l-1] + 1 更新 dp[r]。

转移方程:dp[r] = max(dp[r], dp[l-1] + 1) (对于所有满足 mxleft ≥ l 的 l)

最终答案即 dp[n],若 dp[n] < 0 则输出 -1。


#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 1e9;

void solve() {
    int n; cin >> n;
    if(n == 1) {cout << "-1\n"; return;}
    vector<ll> a(n + 1);
    for(int i = 1; i <= n; i ++) cin >> a[i];

    //预处理左右最近的合法数
    vector<int> L(n + 1, 0), R(n + 1, n + 1);
    for(int i = 1; i <= n; i ++) {
        ll k = a[i];
        int l = i - 1, r = i + 1;
        while(l >= 1 && gcd(k, a[l]) == 1) l --;
        while(r <= n && gcd(k, a[r]) == 1) r ++;
        L[i] = l, R[i] = r;
    }

    vector<int> dp(n + 1, -INF); dp[0] = 0;
    for(int i = 2; i <= n; i ++) {
        int mxleft = L[i];
        vector<int> pos;
        for(int j = i - 1; j >= 0; j --) {
            if(R[j] <= i) {
                //统计所有合法的左端点
                if(mxleft >= j) pos.push_back(j);
            }
            else mxleft = min(mxleft, L[j]);
        }
        for(auto& left : pos) {
            if(left == 0) continue;
            dp[i] = max(dp[i], dp[left - 1] + 1);
        }
    }
    cout << (dp[n] <= 0 ? -1 : dp[n]) << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int _=1; //cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-10-03 10:16  thenrify  阅读(7)  评论(0)    收藏  举报