联合猫国
去年模拟赛做过一道几乎一模一样的题,于是一眼秒了。
本题的一个结论:最终可合并的区间数为 \(\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;
}