codeforces 940E Cashback 题解

一个就像贪心一样的 DP
思路还是挺巧妙的

首先由于可以任意分段 对于任何一个长度 \(len<k\) 的段 这一段是不能够去掉任何一个数的 也就是每一个数都要被统计到答案里 所以要是弄出来这样一个分段就把它拆成 \(len\) 个长度为 \(1\) 的段是等价的
拆了处理更方便

其次对于长度 \(len\geq k\) 的段,这里先看长度不为 \(k\) 的整数倍的段
我们可以把它分成 一个长度为 \(mk\)(\(m\) 为满足 \(mk<len\) 的最大整数)的段和几个长度为 \(1\) 的段。
其中长度为 \(mk\) 的段取所有情况中最优情况 其它用长度为1的段填满
与上面同理。

现在我们只剩下长度为 \(1\) 的段和长度为 \(mk\) 的段了。
接下来 观察后者,如果把它分成 \(m\) 个长度为 \(k\) 的段 结果一定不会更劣。
因为去掉的数不会减小,但可能增大。

所以就分开。

然后这题就变成把一些数任意分成长度为 \(1\) 和 \(k\) 的段,其中每个长度为 \(k\) 的段的贡献(就是和)为这段的和减去它的最小值,长度为 \(1\) 的段就是加上这个数本身了。

然后可以 dp 了 这里大家应该就都会了
$dp[i] = min(dp[i - 1] + w[i],dp[i - c] + sum(i - c + 1,i) - min{w[i-c+1],...,w[i]}) $

最小值用线段树实现了

code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define mid ((l + r) >> 1)
#define lson (x << 1)
#define rson ((x << 1) | 1)
using namespace std;
typedef long long LL;

LL a[500005] = {0};
LL val[2000005] = {0};

void build(LL x,LL l,LL r){
	if(l == r){ val[x] = a[l]; return; }
	build(lson,l,mid);
	build(rson,mid + 1,r);
	val[x] = min(val[lson],val[rson]);
}

LL query(LL x,LL l,LL r,LL L,LL R){
	if(L <= l && r <= R) return val[x];
	LL ret = 0x3f3f3f3f3f3f3f3f;
	if(L <= mid) ret = min(ret,query(lson,l,mid,L,R));
	if(R > mid) ret = min(ret,query(rson,mid + 1,r,L,R));
	return ret;
}

LL n,c;
LL dp[500005] = {0};
LL pre[500005] = {0};

int main(){
	memset(dp,0x3f,sizeof(dp));
	cin >> n >> c;
	for(LL i = 1;i <= n;i ++) cin >> a[i];
	for(LL i = 1;i <= n;i ++) pre[i] = pre[i - 1] + a[i];
	// 前缀和求一段的和
    build(1,1,n); // 线段树别忘了build
	dp[0] = 0;
	for(LL i = 1;i <= n;i ++){
		if(i >= c) dp[i] = min(dp[i],dp[i - c] + pre[i] - pre[i - c] - query(1,1,n,i - c + 1,i));
		dp[i] = min(dp[i],dp[i - 1] + a[i]);
        // dp转移
	}
	cout << dp[n] << endl;
	return 0;
}

所以样例解释不能信啊

posted @ 2020-04-21 13:59  ItzInstallB  阅读(175)  评论(1)    收藏  举报