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

_zuoqingyuan
蒟蒻搬运于
2025-08-24 23:03:00,当前版本为作者最后更新于2024-11-17 12:36:22,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
“你只能做一个操作——减少序列中的一些数字”。
因为没看见这句话瞪这题瞪了两周。
前置:斜率优化 dp,模板题。
思路分析
怎么没有单调队列题解?
设 表示将 修改为 K-Anonymous 序列的最小花费。
对于 ,显然不可能修改为合法序列。
对于 ,我们可以枚举 ,将 修改为一段相同的数字,并将这段的花费累加到 上用来更新 。其中 。
因为我们只能减小某个数字,而序列单调不下降,所以我们只能把 都修改为 。
记 ,则:
$$dp_i=\begin{cases}\infty&i<k\\dp_j+(S_i-S_j)-(i-j)a_{j+1}&i\ge k\end{cases} $$我们主要对后一种情况进行讨论,。
$$dp_i=\min_{0\le j< i-k}\{dp_j+S_i-S_j-(i-j)a_{j+1}\} $$把式子拆开,去掉 。
我们发现式子中只有一项同时包含 。显然可以斜率优化,移项得。
令
则式子可以化为 的形式。因为 单调不下降,所以 均具有单调性,可以直接用单调队列,时间复杂度 。
#include <iostream> #include <cstring> #include <cstdio> #define int long long//防止爆 int using namespace std; const int N=5e5+10; int n,k,a[N],s[N],l,r,q[N],dp[N]; inline int X(int x){return a[x+1];} inline int Y(int x){return dp[x]-s[x]+x*a[x+1];} void work(){ scanf("%lld %lld",&n,&k);k--; for(int i=1;i<=n;i++)scanf("%lld",a+i),s[i]=a[i]+s[i-1]; q[l=r=1]=0;dp[0]=0;//初始化 for(int i=1,j;i<=n;i++){ if(i<=k){dp[i]=0x3f3f3f3f;continue;} while(l<r&&(Y(q[l+1])-Y(q[l]))<=i*(X(q[l+1])-X(q[l])))l++;//防止精度误差 dp[i]=dp[j=q[l]]+s[i]-s[j]-(i-j)*a[j+1]; while(l<r&&(Y(q[r])-Y(q[r-1]))*(X(i-k)-X(q[r]))>=(Y(i-k)-Y(q[r]))*(X(q[r])-X(q[r-1])))r--;//防止精度误差 q[++r]=i-k;//决策入队 } printf("%lld\n",dp[n]); return; } void clear(){//清空 memset(q,0,sizeof(q)); memset(dp,0,sizeof(dp)); l=r=0; return; } signed main(){ int T=0; cin>>T; while(T--){ work(); clear(); } return 0; }如有错误,请指出。
- 1
信息
- ID
- 10132
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者