Luogu P13925 [POKATT 2024] 联合猫国 / The Paw-litical Game 题解 [ 蓝 ] [ 线性 DP ] [ 种类数观察 ]

联合猫国

去年模拟赛做过一道几乎一模一样的题,于是一眼秒了。

本题的一个结论:最终可合并的区间数为 \(\bm{O(n\log n)}\) 级别

证明可以考虑构造出可合并区间数最多的序列,显然是所有数都相同时的区间数,可以取到上界 \(n\log n\)。其余构造方式,例如构造一个 \(n, n - 1, n - 2, \cdots, 1, 1\) 的序列,一段最多只能产生 \(n\) 个可合并区间,显然是无法取到上界的。

因此后面的就是简单的了。注意到当一个数的增量确定时,合并的左端点也随之确定,因此可以用 vector 存每个数增量为 \(\bm x\)合并的左端点。因为可合并区间数是 \(O(n\log n)\) 级别的,因此暴力跳就可以了。

最后从每个可合并的左端点处转移,做一个线性 DP 即可。总体时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 1000005;
int n, a[N], dp[N];
vector<int> f[N];
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        f[i].push_back(i - 1);
        dp[i] = min(dp[i], dp[i - 1] + 1);
        int now = i - 1, val = a[i];
        while(val - a[now] >= 0 && val - a[now] < f[now].size())
        {
            now = f[now][val - a[now]];
            val++;
            dp[i] = min(dp[i], dp[now] + 1);
            f[i].push_back(now);
        }
    }
    cout << dp[n];
    return 0;
}
posted @ 2025-10-28 20:26  KS_Fszha  阅读(3)  评论(0)    收藏  举报