专栏文章
题解:P5665 [CSP-S2019] 划分
P5665题解参与者 3已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @mioz0unc
- 此快照首次捕获于
- 2025/12/03 03:28 3 个月前
- 此快照最后确认于
- 2025/12/03 03:28 3 个月前
思路分析
首先,我们有一个结论:划分的段数越多越好。
如何证明呢?可以感性理解:若将 分成 段,则所有的方案中,最后一段小一点的会更优。而分的段数越多,最后一段就可以越小,所以我们会让分的段数尽可能的多。
如何求最小段数呢?一个直观的 dp 思路是,设 将 划分,最后一段右端点为 ,倒数一段右端点为 ,直接转移即可,时间复杂度 ,可以拿到 的分数。
状态难以简化,不妨从贪心角度考虑,我们从前往后取。如果当前段的和大于我们上一个划分的段,则令答案加一,开始划分新的一段,反之则将一个新的元素放入当前段。这个贪心实际上是错的,可以 Hack 掉。
例如给定的数列是 ,我们的贪心策略划分的结果是 ,但实际上的最优策略是 ,我们思考贪心算法错误的原因。
倘若要将 这部分划分成三段,则上面的两种算法分别将第三段划为了 和 ,这两种方法都是合法的,但是第二种划分这一段的和更小,所以对于后面的转移更优。所以,我们划分时,不仅要让段数多的情况下合法,还要让最后一段的和尽可能小。
我们依据这个贪心思路重新设计 dp。设 表示将 合法划分的最大段数, 表示将 划分成 的基础上,最小化最后一段的总和,同时记录序列 的前缀和 ,则可以得到 的转移式为 满足 。每求出 后,我们都求出 , 就是所有满足所有满足 中 的最小值。暴力跑是 ,可以得到 的分数。
如何优化转移的过程呢?容易发现一个特点, 都是单调不降的。因为无论如何只需要将 加入最后一段, 就可以继承 的值,而 ,所以 也具有单调性。因为我们要找到最大的 ,所以我们只需要找到最后一个 满足 即可最大化 的同时,最小化 。
而对于 移项可得 。对于 ,若有 ,则 一定比 优,则维护一个 单调的栈,每次计算 时则找到最后一个 ,容易发现这个最优决策 实际上也是单调的,所以可以换成单调队列。
最后统计方案时,只需记录每个点的最优决策 ,然后回溯计算即可,时间复杂度 。
下面给出几点卡常建议:
-
用 代替 , 也可以省去。
-
我们对于 ,实际上不需要开 三个数组,直接用同一个数组生成 ,计算前缀和 。
Code
CPP#include <iostream>
#include <cstdio>
using namespace std;
typedef __int128 i128;
typedef long long ll;
const int N=4e7+5;
const int M=1e5+10;
const ll mod=(1<<30);
int f[N],p[N],q[N];
ll s[N];
int n,type,m;
i128 ans=0;
inline ll g(int x){
return s[x]-s[p[x]];
}
void init(){
scanf("%d %d",&n,&type);
if(type==0){
for(int i=1;i<=n;i++)scanf("%lld",s+i);
}else{
long long x,y,z,pp,l,r,lstp=0;
scanf("%lld %lld %lld %lld %lld %d",&x,&y,&z,s+1,s+2,&m);
for(int i=3;i<=n;i++)s[i]=(x*s[i-1]+y*s[i-2]+z)%mod;
for(int i=1;i<=m;i++){
scanf("%lld %lld %lld",&pp,&l,&r);
for(int j=lstp+1;j<=pp;j++)s[j]=(s[j]%(r-l+1))+l;
lstp=pp;
}
}
for(int i=1;i<=n;i++)s[i]+=s[i-1];
return;
}
void write(i128 x){
if(x>9)write(x/10);
putchar(x%10+'0');
};
int main(){
init();
int l=1,r=1;q[1]=0;
for(int i=1;i<=n;i++){
while(l<r&&s[i]>=g(q[l+1])+s[q[l+1]])l++;
p[i]=q[l],f[i]=f[p[i]]+1;
while(l<=r&&g(q[r])+s[q[r]]>=g(i)+s[i])r--;
q[++r]=i;
}
for(;n;n=p[n])ans+=(i128)(s[n]-s[p[n]])*(s[n]-s[p[n]]);
write(ans);
return 0;
}
如有错误,请指出。
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...