loading

CF2183H Minimise Cost

本来要和 G 一块放在杂题里写的,但这题太深刻了,于是把它单拎出来了。

题意

给定一个序列 \(a\),你需要选出恰好 \(k\) 个子序列使得不交且并集是全集。定义一个序列的权值是长度乘总和,一种划分方案的权值是划分出的子序列的权值之和,求划分的权值最少是多少。

\(n\le 2\times10^5,\lvert a_i\rvert\le 10^9\)

分析

发现子序列根本没法做,直接考虑找性质。你发现对于两个元素 \(x<y\)\(x\) 所属子序列长度长于 \(y\) 所属子序列。否则交换 \(x,y\) 得到更优解。

根据这个性质我们能得出一个重要性质:一定存在最优解使得选出的是在升序排序下的连续子串。否则若两个子序列在值域上有交,假设两个子序列长度分别为 \(l_1,l_2\),设子序列 \(1\) 是包含较小值的那个,那么根据性质有 \(l_1,l_2\),此时将两个子序列的元素揉一块然后把前 \(l_1\) 个元素分给第一个子序列,后面的分给第二个子序列,算一算贡献差可以发现变更优了。

那么我们先排个序就能 \(O(n^2k)\) 的做这个问题了。当然这过不了,继续优化,你发现当 \(a_i\) 非负时满足四边形不等式,是有决策单调性的,并且由于权值函数满足四边形不等式,答案关于 \(k\) 是有凸性的,所以若 \(a_i\) 非负直接 wqs 套二分队列即可。

\(a_i\) 有负数咋办。你再次注意到一个性质:所有负数必然在一个块内。否则若存在一个负数跟其他正数放在一块,那么把这个负数和其他负数放在一块更优。所以在足够分的情况下,你把负数全缩在一起就可以了。若不够分,说明将正数都单独成块也不够要求的块数,此时需要特判,答案是后缀 \(k-1\) 的和与前面 \(n-k+1\) 个数成块的贡献之和。

但是你发现在只有第一个元素负数的情况下还是不满足四边形不等式。然而你发现,第一段可以大力贪心:考虑当前第一个元素(是正数)是否要放进第一个块里面,只需要判断一下单出和并入的答案哪个小即可,跑出第一个单出更优的点,从这个点开始 dp 即可,现在没有负数了,所以决策单调性成立。体现在代码中,就是从最后一个负数开始做决策单调性,但我们先跟决策点 0 比较,如果 0 更优,那么这个决策点没用;否则,把 0 弹出,说明找到了第一个单出更优的点,把这个决策点压进去。具体可以看代码。

复杂度 \(O(n\log n\log V)\),注意开 __int128

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<numeric>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<array>
#include<tuple>
#include<ctime>
#include<random>
#include<cassert>
#include<chrono>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define j0 jj0
#define j1 jj1
#define y0 yy0
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define deb(val) cerr<<#val<<'='<<val<<','
#define debl(val) cerr<<#val<<'='<<val<<'\n'
#define lowb lower_bound
#define uppb upper_bound
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
#define int __int128
using namespace std;
using namespace __gnu_pbds;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline bool ckmx(T1 &qwqx,T2 qwqy){return qwqx>=qwqy?0:(qwqx=qwqy,1);}
template<typename T1,typename T2>inline bool ckmn(T1 &qwqx,T2 qwqy){return qwqx<=qwqy?0:(qwqx=qwqy,1);}
inline auto rd(){
	int qwqx=0,qwqf=1;char qwqch=getchar();
	while(qwqch<'0'||qwqch>'9'){if(qwqch=='-')qwqf=-1;qwqch=getchar();}
	while(qwqch>='0'&&qwqch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+qwqch-48;qwqch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char qwqch='\n'){
	if(qwqx<0){qwqx=-qwqx;putchar('-');}
	int qwqy=0;static char qwqz[40];
	while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}
	while(qwqy--){putchar(qwqz[qwqy]);}if(qwqch)putchar(qwqch);
}
bool Mbg;
const int mod=998244353;
template<typename T1,typename T2>inline void adder(T1 &x,T2 y){x+=y,x=x>=mod?x-mod:x;}
template<typename T1,typename T2>inline void suber(T1 &x,T2 y){x-=y,x=x<0?x+mod:x;}
const int maxn=2e5+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,m,k,a[maxn],s[maxn];
int f[maxn],g[maxn];
pii que[maxn];
int hd,tl;
int calc(int K){
	auto calc=[&](int l,int r)->int{return f[l]+(s[r]-s[l])*(r-l)-K;};
	que[hd=tl=1]={0,m};
	int p=n,sum=s[m];
	rep(i,m,n){
		f[i]=calc(que[hd].fi,i),g[i]=g[que[hd].fi]+1;
		que[hd].se++;
		if(hd<tl&&que[hd].se>=que[hd+1].se)++hd;
		while(hd<=tl&&calc(i,que[tl].se)<calc(que[tl].fi,que[tl].se))--tl;
		if(hd>tl){que[++tl]={i,i+1};continue;}
		int l=que[tl].se,r=n,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(calc(i,mid)<calc(que[tl].fi,mid))res=mid,r=mid-1;
			else l=mid+1;
		}
		if(~res)que[++tl]={i,res};
	}
	return g[n];
}
inline void solve_the_problem(){
	n=rd(),k=rd();
	rep(i,1,n)a[i]=rd();
	sort(a+1,a+n+1);
	rep(i,1,n)s[i]=s[i-1]+a[i];
	m=0;
	rep(i,1,n)if(a[i]<0)++m;
	if(k>=n-m+1){
		int ans=0,p=n;
		while(k>1)ans+=a[p],--p,--k;
		ans+=s[p]*p;
		return write(ans);
	}
	ckmx(m,1);
	int l=-1e20,r=1e20,res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(calc(mid)<=k)res=mid,l=mid+1;
		else r=mid-1;
	}
	calc(res);
	write(f[n]+k*res);
}
bool Med;
signed main(){
//	freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=rd();
	rep(__,1,_)solve_the_problem();
}
/*

*/

posted @ 2026-01-13 18:16  dcytrl  阅读(11)  评论(0)    收藏  举报