题目大意
给定长度为
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−(j−1)di
再给出
q
q
q个数
m
m
m,问买m个糖的最小价值为多少。
思路:
首先得推出一个结论:最优方案至多存在一种糖使得它被买的个数在1到
c
i
−
1
c_i-1
ci−1之间。
证明:假设最优方案中有
i
i
i物品和
j
j
j物品,它们选取的个数
x
,
y
x,y
x,y在1到
c
x
−
1
c_x-1
cx−1和
c
y
−
1
c_y-1
cy−1之间,那么满足
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
ci−1在左边还是右边,然后背包进行转移。
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;
}