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;
}

浙公网安备 33010602011771号