[EZEC-11] Tyres

一道简单题,然后我想了几个小时还想不出……

尽管早就有心理准备,但是一发题解:我是 sb

[EZEC-11] Tyres

题意:给你 \(n\) 种物品,对于每一种物品选了一个以上就要支付 \(t\) 的代价,第 \(i\) 种物品选第 \(j\) 个的代价为 \(a_i+b_i(j-1)^2\) ,求选 \(m\) 个的最小代价

这道题如果考虑每种物品选的个数,就是经典的分组背包问题(而且可以分治优化决策单调性:珠宝),但是复杂度太高

如果去掉 \(t\) 的代价,那么就有一个更经典的贪心:每次选最小的代价即可

考虑证明,由于每种物品价值单调上升,看成归并排序,每次的最小值都在队头产生,每次都选的是全局最小值,当然代价最小

发现这个性质依赖于物品价值单调上升,而加上 \(t\) 的限制无非是破坏队头的单调性

当我们注意到这里 \(t\leq 500\) 而价值是一个增长很快的二次函数,就会发现如果我们把队头的几个数(在 \(O(\sqrt t)\) 级别)打包成一个数,就会满足决策单调性了

设这个打包的数的个数为 \(B\) ,大于 \(B\) 的部分不受 \(t\) 影响跑归并,小于等于 \(B\) 的部分算 \(t\) 的贡献分组背包,现在就是要决策对于每一个物品种类,是选大于 \(B\) 个还是小于等于 \(B\) 个,发现将两个部分的答案拼在一起时自动完成了这个决策过程,因为大于 \(B\) 的每一次决策的贡献严格比小于等于 \(B\) 的每一次决策贡献要大(大概理解下就是每次总会优先选小于等于 \(B\) 的,当一个物品种类在归并的过程中被记入答案了,那么它小于等于 \(B\) 的个数一定都选完了,所以我们并不需要关心我们具体哪些物品种类需要选大于等于 \(B\) 次)

Code

#include <cstdio>
#include <queue>
using namespace std;
int read(){
    //reading……
}
const int N=503;
const int M=200003;
const int Sq=25;
typedef long long ll;
int n,m,t;
int a[N],b[N],c[N];
ll vl[N][Sq+3],f[N*Sq],g[M];
struct cmp{
	bool operator()(const int x,const int y){
		return a[x]+1ll*b[x]*c[x]*c[x]>a[y]+1ll*b[y]*c[y]*c[y];
	}
};
priority_queue<int,vector<int>,cmp> q;
int main(){
	n=read();m=read();t=read();
	for(int i=1;i<=n;++i){
		a[i]=read();b[i]=read();c[i]=Sq;vl[i][1]=a[i]+t;
		for(int j=1;j<Sq;++j) vl[i][j+1]=vl[i][j]+a[i]+1ll*b[i]*j*j;
		q.push(i);
	}
	for(int i=1;i<=n*Sq&&i<=m;++i) f[i]=0x3f3f3f3f3f3f3f3f;
	int sm=0;ll res=0x3f3f3f3f3f3f3f3f;
	for(int i=1;i<=n;++i){
		sm+=Sq;if(sm>m) sm=m;
		for(int j=sm;j;--j)
			for(int k=1;k<=Sq&&k<=j;++k)
				if(f[j-k]+vl[i][k]<f[j])
					f[j]=f[j-k]+vl[i][k];
	}
	for(int i=1;i<=m;++i){
		int x=q.top();q.pop();
		g[i]=g[i-1]+a[x]+1ll*b[x]*c[x]*c[x];
		++c[x];q.push(x);
	}
	for(int i=0;i<=sm&&i<=m;++i)
		if(f[i]+g[m-i]<res) res=f[i]+g[m-i];
	printf("%lld\n",res-t);
	return 0;
}
posted @ 2022-03-07 22:09  yyyyxh  阅读(156)  评论(0编辑  收藏  举报