CF1945D 题解

题意简述

n 个人在排队,每个人有两个属性 ai,bi。有一个人站在这 n 个人的后面,他想要进到前 m 个位置中的任何一个。他可以进行任意次数的以下操作:

  • 假设他在位置 x,他可以选择一个 y<x,和他交换位置。一次交换的费用是 by+k=y+1x1ak

问他达成目标的最小费用是多少。

解法

考虑 dp。

先把 ai 用前缀和维护到 si 数组。

fi 为从队尾换到第 i 个位置的最小花费,显然有:

fi=minj>i{fj+bi+sj1si}

初始状态是 fn+1=0

这么转移是 O(n2) 的,肯定过不了,考虑优化。

先把常数项(和 j 无关的项)提出:

fi=minj>i{fj+sj1}+bisi

现在问题就转化为:对于每一个 i 求一个 x=min{fj+sj1},然后 fi=x+bisi 即可。

显然,这个 x 可以用后缀最大值维护。

于是我们就 O(n) 解决了这个问题。

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10,INF=0x3f3f3f3f3f3f3f3f;
int n,m;
int a[N],b[N];
int f[N];
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	for(int i=1;i<=n;i++)b[i]+=b[i-1];
	f[n+1]=0;
	int mn=b[n];
	for(int i=n;i>=1;i--){
		f[i]=mn+a[i]-b[i];
		mn=min(mn,f[i]+b[i-1]);
	}
	int ans=INF;
	for(int i=1;i<=m;i++)ans=min(ans,f[i]);
	cout<<ans<<'\n';
	memset(f,0,sizeof(int)*n+10);
	memset(b,0,sizeof(int)*n+10);
	memset(a,0,sizeof(int)*n+10);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;
}
posted @   Linge_Zzzz  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示