1 条题解
-
0
自动搬运
来自洛谷,原作者为

哈哈人生
Go Best.搬运于
2025-08-24 23:06:20,当前版本为作者最后更新于2024-11-23 20:30:25,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题外话
好久没写题解了,碰巧遇到了这题。
这题挺有意思的。思路
读完题后,可发现最终答案和不同数字的个数有关,和具体数值无关,所以先把 离散化。然后考虑动态规划,设 表示前 个数分段所得的最小值。先写个最简单的思路,易得转移式为 ,其中 , 表示的是 到 之间不同数字的个数。对于每个 ,从大到小枚举 ,顺便用桶记录不同数字的个数, 的做法就被我们找到了,加一个小优化可得 分。
#include<bits/stdc++.h> #define int long long using namespace std; int n,a[200005],b[200005],dp[200005]; int lsh[200005],tot=0; signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n; for(int i=1;i<=n;i++)cin>>a[i],lsh[++tot]=a[i]; sort(lsh+1,lsh+tot+1); tot=unique(lsh+1,lsh+tot+1)-lsh-1; for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+tot+1,a[i])-lsh; for(int i=1;i<=n;i++){ dp[i]=1e18; int w=1; b[a[i]]=i; for(int j=i-1;j>=0;j--){ if(w*w>=dp[i])break;//小优化 dp[i]=min(dp[i],dp[j]+w*w); if(b[a[j]]!=i)w++,b[a[j]]=i; } } cout<<dp[n]; return 0; }那这个思路是不是陷入瓶颈了呢?再重新想一下哪些转移位置是真的有用的。设 是从某个位置 上转移来的,而 若满足 是在 到 中出现过的,那么其显然不如从位置 上转移,因为多一个已出现的 无任何增加的花费,所以此时位置 无用,也就是说最优的 一定不是从 转移过来的。把这个规律普遍化一下,就会发现对于 来说,一个位置 真正有用就需要满足不存在 ,使得 。我们可以用一个桶来存所有数字最后出现的位置,再用
set来维护之间的先后顺序,每次转移都从set中所存的位置一一尝试选取最优解。但这样乍一看,set的大小最多也是 ,似乎还是 的时间复杂度。但别忘了我们还有一个小优化:if(w*w>=dp[i])break;。之前这个优化的作用不明显是因为每个位置可能数字出现过也有可能没出现过,构造特定数据还是能使循环执行接近 次。但这回我们每访问set中的一个位置, 必定加一,因为我们存的是每个数字最后一次出现的位置。所以这个代码就使我们的时间复杂度变为 ( 的最大取值也不过是 ,而且显然会小很多),并且大概率跑不满。正解代码:
#include<bits/stdc++.h> #define int long long using namespace std; int n,a[200005],b[200005],dp[200005]; int lsh[200005],tot=0; set<int> s; typedef int type; inline type read(){ type x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch)){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void write(type x){ if(x<0)putchar('-'),x=-x; if(x>9)write(x / 10); putchar(x%10+'0'); } signed main(){ n=read(); for(int i=1;i<=n;i++)a[i]=read(),lsh[++tot]=a[i]; sort(lsh+1,lsh+tot+1); tot=unique(lsh+1,lsh+tot+1)-lsh-1; for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+tot+1,a[i])-lsh; for(int i=1;i<=n;i++){ dp[i]=1e18; if(b[a[i]])s.erase(s.find(-b[a[i]])); int w=1; for(auto it=s.begin();it!=s.end();it++){ int j=-*it; dp[i]=min(dp[i],dp[j]+w*w); w++; if(w*w>=dp[i])break; } dp[i]=min(dp[i],w*w); b[a[i]]=i,s.insert(-i);//存-i是在使set从大往小排列位置 } write(dp[n]); return 0; }后记
点个赞吧。
- 1
信息
- ID
- 9291
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者