【题解】洛谷P11311、P2943: 漫长的小纸带、Cleaning Up G

赛时不会去想 dp,感觉没法转移,然后去写了贪心,然后直接假掉唐完了

为什么贪心不能做,因为多个数的话还是可能被减,\(3\) 个数长度为 \(11\) 就可以变成 \(9\),非常划算,好像很显然,但是为什么我赛时写了只会有长度 \(2\) 的区间唐完了

考虑 dp,设 \(f_i\) 表示 \(1-i\) 的最小代价,枚举上一次分段的位置 \(j\),有代价转移 \(f_i=\min_{j=1}^{i} f_{j-1}+g(i,j)^2\),其中 \(g(i,j)\)\(i\)\(j\) 之间不同的数的个数,这样做的复杂度为 \(O(n^2)\)

考虑优化,其中最耗时的是求区间不同的数的个数, \(i\) 是上界,所有 \(g(i,j)\le \sqrt{i}\) 时才有意义,所有我们可以枚举区间不同的数的个数,对于 \(j\)\(pos_j\) 表示 \(i-pos_j\)\(j\) 个不同的数个数的最右端点,所有我们转移方程可以变化了 \(f_i=\min_{j=1}^{\sqrt{i}} f_{pos_j-1}+j^2\),这样复杂度 \(O(n\sqrt{n})\)

现在我们要维护右端点了,每加入一个数,\(g(i,j)\) 单调不降,我们就可以移动指针,这里是类似莫队的思想,不断右移指针直到合法,总复杂度 \(O(n)\),一共有 \(sqrt{n}\) 个指针,所以维护的总复杂度为 \(O(n\sqrt{n})\)

因为移动指针要存桶,而数据范围很大要先离散化一下,代码全部复杂度为 \(O(n+n\log n+n+n\sqrt{n}+n\sqrt{n})\)

第一题的复杂度为 \(2e5+2e5×17+2e5+2e5×450 +2e5×450 =183,800,000\)

范围大的一维放前面,连续内存访问常数小。

#include <bits/stdc++.h>
#define ll long long
//#define int ll
//#define ls p<<1
//#define rs p<<1|1
#define re register
const int N=2e5+10;
//const int mod=998244353;
using namespace std;
int n;
int a[N];
ll f[N];
int cnt[450];
int b[N][450];
int pos[450];
int po[450];
int c[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(re int i=1;i<=n;i++){
cin>>a[i];
c[i]=a[i];
f[i]=1e18;
}
sort(c+1,c+1+n);
int tot=unique(c+1,c+n+1)-c-1;
for(re int i=1;i<=n;i++){
a[i]=lower_bound(c+1,c+tot+1,a[i])-c;
}
f[0]=0;
for(re int i=1;i<=448;i++){
po[i]=i*i;
}
for(re int i=1;i<=n;i++){
for(re int j=1;j<=448;j++){
++b[a[i]][j];
if(b[a[i]][j]==1){
++cnt[j];
if(cnt[j]>j){
while(--b[a[pos[j]]][j]!=0) ++pos[j];
++pos[j];
cnt[j]=j;
}
}
if(cnt[j]==j){
f[i]=min(f[i],f[pos[j]-1]+po[j]);
}
}
}
cout<<f[n];
return 0;
}
posted @   sad_lin  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示