CF1355E Restorer Distance
题目解析
好萎靡呀,又是除了我全场都会系列
为啥要思维定势死磕dp咧(主要是想到了一个类似的题用dp做的 但其实完全不一样啊
讲个笑话:我看出来了长得像二次函数,但是没想到三(啊 我写的san 好像跟删除线叠了)分
首先,如果固定一个所有砖块最后的高度,我们可以在复杂度内算出需要的代价。
具体来说,如果,那操作三就不用。否则,我们扫一遍可以知道每块砖变成目标值需要多少次操作一/操作二,把操作一和操作二尽量配对成操作三,剩下的单独操作就可以了。(当然,在写法上,可以直接把,然后直接算,不判断(押个韵
然后,我们就得到了一个的零分优秀算法。
稍微优秀一点,可以将初始高度排序,然后预处理一下前缀和,每次就可以得出一个的代价。
这个时候我们盲猜:如果比较大(具体指),也就是不用操作三,那么操作一和操作二的个数没有影响(这个意思是说,不需要尽量让操作一和操作二的数量平均然后去凑更多的操作三,如果操作三更优的话,说不定可以改变一下,凑更多的操作三出来,结果更优),所以最终的这个一定是初始的个之一;但如果比较小,就不一定了。但是我们考虑到要尽量凑操作三出来,所以最后的会落在平均数的附近,枚一枚就好喏。
然后,结合前面的预处理,我们就得到了一个看起来不太靠谱,但实际上可以满分的优秀算法。
最后,说一下我是怎么觉得它是一个类似于二次函数的东西的呢?
感性理解:
之前相当于是我们把操作三变成了操作一和操作二,你贪心地想,如果操作一代价大于操作二代价,那要选小一点,否则选大一点,但是选太小或者太大也不太行的样子。因为操作数变多了,加起来可能反而不优。举个例子,假如是操作一代价大于操作二代价,而我如果把选很小,我每把减小,右边的数需要更多的操作二,左边的数需要更小的操作一,但是右边的数比左边的数要多,加起来的总代价可能还不如我右边少几次操作二,左边多几次操作一来得好。(更何况还有操作一和操作二数量更接近凑更多操作三出来使答案更优的情况)而这玩意儿就很像初中的二次函数应用题,提高价格顾客变少的那种问题,总括来说就是:是的一次函数,总收益/代价是可能再加减乘除个常数什么的,不过那不重要,重要的是最终是的二次函数。
理性理解:
设最终的高度为,需要增加次,减少次,我们和比较(求个斜率什么的),就可以知道增减情况和变化率。设变为时的操作一的变化量为,则新的,而为所有砖块中高度小于等于的砖块的个数,那么操作二的变化量为,则
- 时:,(分母除以就是斜率,所以这玩意儿就是斜率,或者说导数)
- 时:,
那么关于的函数的斜率是一个一次函数(说准确点是分段函数),是随的增大而增大的分段函数。而且斜率逐渐变大,(先负后正,刚开始很小的时候,)的一次函数,所以应该是类似于开口朝上的二次函数,不过因为是整数,并且它关于不是连续变化的,所以斜率也不是连续变化的,而是在一段区间内斜率一样,那么就是一个下凸包(是一截一截的,而不是二次函数那样比较光滑的曲线)。
但是由于我对三分这个算法太不熟悉了,所以并没有想到它嘤嘤嘤。
事实上,我们之前说了斜率是一次函数,那么可以还对斜率进行二分。实际上,三分和二分斜率是等价的,因为单峰函数的斜率是单调的,并且在最值斜率为。
然后,你就得到了一个看以来要靠谱一些的满分做法。
(我是不是跟分治有仇啊 儒略历那道题用二分要简单多了 但我就是没有想到二分 然后直接计算模拟硬刚 最后惨挂
►Code View
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define M 200005
#define MOD 998244353
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
LL rd()
{
LL x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
int n;
LL a,r,m,h[N],ans=INF;
LL opt1,opt2;
void calc(int i,int j)
{
if(h[i]==j) return ;
if(h[i]<j) opt1+=j-h[i];
else opt2+=h[i]-j;
}
LL work()
{
if(m<a+r)
{
LL k=min(opt1,opt2),res=0;
res=k*m,opt1-=k,opt2-=k;
if(opt1) res+=opt1*a;
if(opt2) res+=opt2*r;
return res;
}
else return opt1*a+opt2*r;
}
LL f(LL het)
{
opt1=opt2=0;
for(int i=1;i<=n;i++)
calc(i,het);
return work();
}
int main()
{
//freopen("bricks.in","r",stdin);
//freopen("bricks.out","w",stdout);
n=rd(),a=rd(),r=rd(),m=rd();
for(int i=1;i<=n;i++)
h[i]=rd();
sort(h+1,h+n+1);
int l=h[1],r=h[n];
while(l+10<r)
{
int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
LL lans=f(lmid),rans=f(rmid);
if(lans>rans) l=lmid;
else r=rmid;
}
LL ans=INF;
for(int i=l;i<=r;i++)
ans=min(ans,f(i));
printf("%lld\n",ans);
return 0;
}
/*
3 100 100 1
1 3 8
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现