【CSP-S 2019】【洛谷P5665】划分

1|0前言


csp时发现自己做过类似这道题的题目 : P4954 [USACO09Open] Tower of Hay 干草塔
然后回忆了差不多15min才想出来。。。
然后就敲了88pts的部分分。当时的内存是950MB左右,写一个高精就炸内存了。

2|0题目


2048 年,第三十届 CSP 认证的考场上,作为选手的小明打开了第一题。这个题的样例有 n 组数据,数据从 1n 编号,i 号数据的规模为 ai

小明对该题设计出了一个暴力程序,对于一组规模为 u 的数据,该程序的运行时间u2。然而这个程序运行完一组规模为 u 的数据之后,它将在任何一组规模小于 u 的数据上运行错误。样例中的 ai 不一定递增,但小明又想在不修改程序的情况下正确运行样例,于是小明决定使用一种非常原始的解决方案:将所有数据划分成若干个数据段,段内数据编号连续,接着将同一段内的数据合并成新数据,其规模等于段内原数据的规模之和,小明将让新数据的规模能够递增。

也就是说,小明需要找到一些分界点 1k1<k2<<kp<n,使得

i=1k1aii=k1+1k2aii=kp+1nai

注意 p 可以为 0 且此时 k0=0,也就是小明可以将所有数据合并在一起运行。

小明希望他的程序在正确运行样例情况下,运行时间也能尽量小,也就是最小化

(i=1k1ai)2+(i=k1+1k2ai)2++(i=kp+1nai)2

小明觉得这个问题非常有趣,并向你请教:给定 nai,请你求出最优划分方案下,小明的程序的最小运行时间。

3|0思路:


假设我们现在已经划分为三个部分x,y,z满足xyz,那么显然是有

x2+y2+z2<x2+(y+z)2

所以我们肯定要做到能分就分
但是贪心选取肯定是错误的。样例一明显就指出了错误。
考虑dp。设f[i]表示我们划分1i的所有数,以i为最后一个区块的情况下,最后一个区块的最小长度。
那么枚举一个j,明显有

f[i]=min(k=jiak)   (k=jiakf[j])

由于k=jiak满足单调性,所以肯定选择尽量大的j来转移。
如果j可以转移到i,那么转移的同时可以求出划分的费用

ans[i]=ans[j1]+(k=jiak)2

这样我们就得到了一个O(n2)的算法,可以得到64pts的高分。
我们发现,转移的条件k=jiakf[j]其实就是k=1iakk=jiakf[j],移项就得到了k=1iakf[j]+k=jiak
我们发现,在做了前缀和之后,上式等号左边只与i有关,等号右边只与j有关。同时我们又要满足选择尽量大的j来转移,所以就可以维护一个单调队列装f[j]+k=jiak进行转移。
但是我们每次要选择的是满足k=1iakf[j]+k=jiak的尽量大j进行转移,而不是单纯的最小的k=1iakf[j]+k=jiak转移。所以我们每次要不断弹出队头,知道队头不再满足k=1iakf[j]+k=jiak。此时将最后一次弹出的元素再从头部插入进行转移。这样就保证了每次选择j最大的满足条件的元素进行转移。容易证明,弹出的元素不会对后面的转移产生影响。
这样每个元素最多进入队列1次,时间复杂度O(n)
这样我们就得到了88pts的高分。
对于type=1的数据点需要使用高精,但是由于O(n)的算法我们的内存已经使用了950MB,所以几乎没有空间来敲高精。
所以此时就只能用csp不允许使用的__int128了
我们将ans改为__int128类型,是可以存下最终答案的。
然后我就愉快的T了。
在这里插入图片描述
在尝试过所有我知道的化学性卡常后,样例三依然需要2.4s+才可以跑过。
所以此时就只能用csp不允许使用的Ofast了
物理性卡常nb!样例最终可以在0.85s左右跑过。
然后我就愉快的MLE了。
在这里插入图片描述
经过输出后,ans,f,sum三个数组加起来是1200+MB。将近200MB的差距,只能考虑删除一个数组了。
anssum是肯定无法删除的,而f我们发现,在转移时是等于sum[i]sum[last]的,其中last直可以转移的最大的j
所以我们考虑直接用sum数组来表示出f数组。这样我们往单调队列插入时就要插入i,fi+sumi两维,因为后者在去掉f数组后是没办法算出来的。
最终还是以1.38s,804.98MB过了这道题。但是在csp时是不允许用__int128Ofast的,所以其实这份代码无论是时间还是空间都是过不去的

4|0代码:


#pragma GCC optimize("Ofast") #pragma GCC optimize("inline") #include <queue> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #define mp make_pair using namespace std; typedef long long ll; const int N=40000010,MOD=(1<<30),M=100010; int n,x,type,cnt,id; ll d,xx,yy,zz,m,sum[N],b[4],p[M],l[M],r[M]; __int128 ans[N]; pair<int,ll> last; char ch; deque<pair<int,ll> > q; inline ll read() { d=0; ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar(); return d; } inline void write(__int128 x) { if (x>9) write(x/10); putchar(x%10+48); } inline ll Get(int i) { int id=(i-1)%3+1; if (i>2 && id==1) b[1]=(xx*b[3]+yy*b[2]+zz)%MOD; if (i>2 && id==2) b[2]=(xx*b[1]+yy*b[3]+zz)%MOD; if (i>2 && id==3) b[3]=(xx*b[2]+yy*b[1]+zz)%MOD; //减少模运算次数 if (i>p[cnt]) cnt++; return (b[id]%(r[cnt]-l[cnt]+1))+l[cnt]; } int main() { scanf("%d%d",&n,&type); if (type) { xx=read(); yy=read(); zz=read(); b[1]=read(); b[2]=read(); m=read(); for (int i=1;i<=m;i++) p[i]=read(),l[i]=read(),r[i]=read(); } q.push_back(mp(0,0)); for (register int i=1;i<=n;i++) { sum[i]=sum[i-1]+(type?Get(i):read()); while (q.size() && sum[i]>=q.front().second) { last=q.front(); q.pop_front(); } q.push_front(last); int pos=last.first; ans[i]=ans[pos]+(__int128)(sum[i]-sum[pos])*(sum[i]-sum[pos]); while (q.size() && q.back().second>=sum[i]-sum[pos]+sum[i]) q.pop_back(); q.push_back(mp(i,sum[i]-sum[pos]+sum[i])); } write(ans[n]); return 0; }

__EOF__

本文作者stoorz
本文链接https://www.cnblogs.com/stoorz/p/12076749.html
关于博主:菜死了 /fad
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   stoorz  阅读(635)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示