【CLYZ集训】买棉花糖【分治】【背包】

题目大意

给定长度为 n n n的数组 a i , c i , d i a_i,c_i,d_i ai,ci,di,代表编号为i的糖有 c i c_i ci个,买第 j j j个的价值为 a i − ( j − 1 ) d i a_i-(j-1)d_i ai(j1)di
再给出 q q q个数 m m m,问买m个糖的最小价值为多少。

思路:

首先得推出一个结论:最优方案至多存在一种糖使得它被买的个数在1到 c i − 1 c_i-1 ci1之间。
证明:假设最优方案中有 i i i物品和 j j j物品,它们选取的个数 x , y x,y x,y在1到 c x − 1 c_x-1 cx1 c y − 1 c_y-1 cy1之间,那么满足 b i , x < b j , y + 1 b_{i,x}<b_{j,y+1} bi,x<bj,y+1 b j , y < b i , x + 1 b_{j,y}<b_{i,x+1} bj,y<bi,x+1(其中 b i , j b_{i,j} bi,j是第i种糖选第j个的价值)。这和数组 b b b的定义是矛盾的,所以得证。
那么就直接分治,分类那个1到 c i − 1 c_i-1 ci1在左边还是右边,然后背包进行转移。

c o d e code code

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long

using namespace std;

const ll MAXN = 500 << 2;

ll n, q;
ll a[MAXN], c[MAXN], d[MAXN];
ll f[MAXN][20100];

void solve(ll k, ll l, ll r) {
	if(l == r) {
		for(ll i = 0; i <= c[l]; i ++) f[k][i] = a[l] * i - i * (i - 1) / 2 * d[l];
		for(ll i = c[l] + 1; i <= 20000; i ++) f[k][i] = 1e18;
		return ;
	}
	ll mid = l + r >> 1;
	solve(k << 1, l, mid);
	for(ll i = mid + 1; i <= r; i ++)
		for(ll j = 20000 - c[i]; j >= 0; j --)
			f[k << 1][j + c[i]] = min(f[k << 1][j + c[i]], f[k << 1][j] + a[i] * c[i] - c[i] * (c[i] - 1) / 2 * d[i]);
	solve(k << 1 | 1, mid + 1, r);
	for(ll i = l; i <= mid; i ++)
		for(ll j = 20000 - c[i]; j >= 0; j --)
			f[k << 1 | 1][j + c[i]] = min(f[k << 1 | 1][j + c[i]], f[k << 1 | 1][j] + a[i] * c[i] - c[i] * (c[i] - 1) / 2 * d[i]);
	for(ll i = 0; i <= 20000; i ++) f[k][i] = min(f[k << 1][i], f[k << 1 | 1][i]);
}

int main() {
	freopen("marshmallow.in", "r", stdin);
	freopen("marshmallow.out", "w", stdout);
	scanf("%lld%lld", &n, &q);
	for(ll i = 1; i <= n; i ++) {
		scanf("%lld%lld%lld", &a[i], &d[i], &c[i]);
		if(c[i] > 20000) c[i] = 20000;
	} 
	solve(1, 1, n);
	while(q --) {
		ll x;
		scanf("%lld", &x);
		printf("%lld\n", f[1][x]);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值