CF1768F Wonderful Jump
大神仙题,2023年度最好题预定。
首先有显然的 dp : 表示跳到 的最小代价。
有 。
试图优化:
经过各种手玩,发现没有决策单调性,线段树也无法维护这种新的复杂的变化,转移次数也可能较多,并不是常见的优化。
发现值域是 , 必然有猫腻:
(其实我最开始觉得的一定是从 转移过来,如果你也这么觉得过,读完这篇题解你会发现它是错的。)
因为平方项的出现,当最小值很大的时候一步跳过去是很劣的是 ,我们会跳一个较大的跨度仅当它的最值很小,否则还不如一步一步跳过去的最坏 级别。
比较两种情况的大小
得知 时才有可能更优。
由于值域是 ,考虑阈值分治:
当 时,直接暴力转移,时间复杂度 。
当 时,根据上面的分析, 时,答案才有可能更优,否则一定是无效的。
引出一个很好猜到的引理:
当 ,且 时,经过 一定比不经过 优 ,因为平方和本来就小于和平方,乘上的系数也不大于后者。
这个引理告诉我们:对于一段连续的相同值的数,如果被经过,先跳到开头,接着一个一个地调到末尾,再跳出去是最优的。
于是我们就只需要考虑 这些 个段的最右端的转移,单调栈维护即可。
但是这么做 WA on 5 了,百思不得其解后我看了官方题解,发现漏掉了一种情况:
时,可能从某个不是单调栈中段最右端的转移且不是 的段处转移来,因为前面那个引理引出的结论成立的条件是 “那一段连续的数是最小值”。
所以当 是最小值时,可能有不同于前两种转移的转移:令左边第一个大于等于的 的数为 ,暴力枚举 转移过来,再之前的都满足最值在转移起点了。
这部分的时间复杂度分析:均摊 。
考虑如下证明:
考虑每个转移起点对复杂度的贡献,这个点最多贡献给 个转移终点,因为相等的点会阻断转移,而向后一定是一段单增的指可能受到贡献,所以总转移的次数为 ,均摊 。
取 时总复杂度为 ,空间 。
代码:
//省略快读及宏定义.
signed main()
{
read(n);
rep(i,1,n) read(a[i]) ;
memset(f,0x3f,sizeof f) ;
f[1] = 0 ;
sta[++top] = 1 ;
int x = sqrt(n) ;
for(int i = 2 ; i <= n ; ++ i) {
int mn = a[i] ;
for(int j = i - 1 ; j >= 1 && j >= i - x - 3 ; -- j) {
// 暴力
upmin(mn , a[j]);
upmin(f[i] , f[j] + sq(i - j) * mn) ;
}
while(top && a[sta[top]] > a[i]) top -- ;
sta[++top] = i ;
for(int j = 1 ; j <= x + 1 && j < top ; ++ j) {
upmin(f[i] , f[sta[j]] + sq(i - sta[j] ) * a[ sta[j]]);
// 从单调栈最右端点转移
}
if(a[i] < x + 3) {
// mn = a[i] ;
for(int j = i - 1 ; j >= 1 ; -- j) {
if(a[i] >= a[j]) break;
upmin(f[i] , f[j] + sq(i - j) * a[i]) ;
}
}
}
rep(i,1,n) wrt(f[i],' ') ;
return 0;
}
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17093849.html,谢谢你的阅读或转载!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步