题解:P11311 漫长的小纸带
P11311 漫长的小纸带 题解
题意
给定一个长为
思路
看到分段、最小代价这些字眼,于是考虑 dp:
设
考虑这个状态可以由那些状态得来,发现可以枚举
考虑怎么优化,我们发现当我们考虑
- 当前块数字个数不大于
\sqrt i ; - 当前块大小为
k+1 时数字个数会增加(否则k+1 一定更优)
于是我们可以改变枚举方法,令以
于是我们有了新的转移方程:
Code
#include <bits/stdc++.h>
using namespace std;
constexpr int maxn = 200010;
int n, a[maxn], l, r, pre[490][maxn], sum, dp[maxn], ls[maxn], tot, t[maxn];
map <int, int> v;
void pu(int x) {//加入
if (!(t[x]++)) {
sum++;
}
return ;
}
void po(int x) {//删除
if (!(--t[x])) {
sum--;
}
return ;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n;//读入
for (int i = 1; i <= n; i++) {
cin >> a[i];
ls[i] = a[i];
}
sort(ls + 1, ls + 1 + n);//本做法必须离散化,要不然双指针就TLE了
ls[0] = 0;
for (int i = 1; i <= n; i++) {
if (ls[i] != ls[i - 1]) {
v[ls[i]] = ++tot;
}
}
for (int i = 1; i <= n; i++) {
a[i] = v[a[i]];
}
for (int i = 1; i <= sqrt(n); i++) {//双指针求pre
l = 1, r = 0;
sum = 0;
for (int j = 1; j <= tot; j++) {
t[j] = 0;
}
for (int j = 1; j <= n; j++) {
pu(a[++r]);
while (sum > i) po(a[l++]);
pre[i][j] = l;
}
}
dp[1] = 1;
for (int i = 2; i <= n; i++) {//dp转移
dp[i] = maxn;
for (int j = 1; j <= sqrt(i); j++) {
dp[i] = min(dp[pre[j][i] - 1] + j * j, dp[i]);
}
}
cout << dp[n];//输出
return 0;
}