P4411 [BJWC2010] 取数游戏 题解

题意简析

给定序列 \(a\),求出选择的使得相邻的两数 \(\gcd \ge L\) 的最长的子序列的长度。

思路解析

一拿到题目,我们就看见有求最长的子序列,我们想到了 LIS,可惜这里有条件才能转移。

\(dp_i\) 为 LIS 长度,状态转移方程为:

\[ dp_i = 1 + \max dp_j \]

这样子做是 \(O(n^2)\) 的,显然不能过。


我们在取 \(\max\) 时,其实有更多地方重复计算,还是可以优化的。

注意到 \(\max\) 的不断操作下,数值是单调不降的,那么我们可以维护 \(fac_d\) 为被 \(d\) 整除的数的 \(dp\) 最大值。

枚举每一个 \(a_i\) 的因数,然后更新 \(fac_d\),当前的 \(dp_i\) 就是 \(fac_d\) 最大值加一。然后,用 \(dp[i]\) 更新满足条件的 \(fac_d\) 即可。

总复杂度 \(O(n \sqrt{\max a_i})\),可以通过此题。

代码实现

#include <bits/stdc++.h>
using namespace std;
const int V = 1e6 + 5;
int n, l, ans, dp[V], mx, res;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> l;
    for (int i = 0, x; i < n; i++) {
        vector<int> fac;
        mx = 0;
        cin >> x;
        for (int d = 1; d * d <= x; d++) {
            if (!(x % d)) {
                if (d >= l) {
                    fac.push_back(d);
                }
                if (x / d != d && x / d >= l) {
                    fac.push_back(x / d);
                }
            }
        }
        for (int j = 0; j < fac.size(); j++) {
            int d = fac[j];
            mx = max(mx, dp[d]);
        }
        res = mx + 1;
        for (int j = 0; j < fac.size(); j++) {
            int d = fac[j];
            dp[d] = max(dp[d], res);
        }
        ans = max(ans, res);
    }
    cout << ans;
    return 0;
}
posted @ 2026-01-20 14:28  TangyixiaoQAQ  阅读(0)  评论(0)    收藏  举报