けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

yukicoder No.979 Longest Divisor Sequence

LIS の亜種だけど、雰囲気違って面白い!

問題へのリンク

問題概要

長さ  N の正の整数列  A_{1}, \dots, A_{N} が与えられる。この部分列であって

  • 狭義単調増加
  • 後の index に出てくるやつは前の index にでてくるやつの倍数になっている

という条件を満たすものの最長の長さを求めよ。

制約

  •  1 \le N, A_{i} \le 3 \times 10^{5}

考えたこと

単純に DP しよう。

  • dp[ v ] := 今見ている値が v のとき、v を最後の要素としたときの最長の長さ

として、v の約数 d (< v) を全部見て

  • chmax(dp[ v ], dp[ d ] + 1)

とすればよい。v = 1 のときは dp[ v ] = 1 とする。計算量は  O(N\sqrt{A}) となる。

#include <bits/stdc++.h>
using namespace std;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

vector<long long> divisor(long long n) {
    vector<long long> res;
    for (long long i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            res.push_back(i);
            long long j = n / i;
            if (j != i) res.push_back(j);
        }
    }
    sort(res.begin(), res.end());
    return res;
}

const int MAX = 310000;
int main() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];
    vector<int> dp(MAX, 0);
    for (auto a : A) {
        if (a == 1) dp[a] = 1;
        else {
            const auto& div = divisor(a);
            for (auto d : div) if (d < a) chmax(dp[a], dp[d] + 1);
        }
    }
    int res = 0;
    for (int i = 0; i < MAX; ++i) chmax(res, dp[i]);
    cout << res << endl;
}