专栏文章
题解:P11311 漫长的小纸带
P11311题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mir0468v
- 此快照首次捕获于
- 2025/12/04 13:34 3 个月前
- 此快照最后确认于
- 2025/12/04 13:34 3 个月前
P11311 漫长的小纸带 题解
题意
给定一个长为 数组 ,将他分成若干段,每段的代价为这一段数字个数的平方,求最小代价。
思路
看到分段、最小代价这些字眼,于是考虑 dp:
设 表示 到 的最小代价, 为 到 的不同数字个数。
考虑这个状态可以由那些状态得来,发现可以枚举 所在块的长度 ,此时 ,状态转移方程有了,便可 dp,复杂度 ,能拿 32pts(卡常可卡到 48pts)
考虑怎么优化,我们发现当我们考虑 到 时,如果分成 块,则总代价为 ,所以不可能存在某一块的数字个数大于 ,所以我们枚举 的时候,有用的 仅当:
- 当前块数字个数不大于 ;
- 当前块大小为 时数字个数会增加(否则 一定更优)
于是我们可以改变枚举方法,令以 为右端的块,数字种类数为 ,块的左端点最左到 。这个东西的大小只有 ,也可以通过双指针 求。
于是我们有了新的转移方程:
Code
CPP#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;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...