【题解】洛谷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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」