D. Progressions Covering(贪心 + 差分)

Problem - D - Codeforces

你有两个数组:一个是由n个0组成的数组a,一个是由n个整数组成的数组b。您可以对数组a应用以下操作任意次数:选择长度为k的a的某个子段,并将等差数列1、2、、k加到这个子段上——即将1加到子段的第一个元素上,将2加到第二个元素上,以此类推。所选子段应在数组a的边界内(即所选子段的左边界为I,则满足条件1 < <l+k-1 < n)。注意,添加的级数总是1,2,。, k,而不是k, k-1,,1或任何其他元素(即,子段的最左边的元素总是增加1,第二个元素总是增加2,以此类推)。你的任务是找到满足条件a所需的最小操作数;2 b;对于从1到n的每个i。注意,条件a b应该同时满足所有元素。输入输入的第一行包含两个整数n和k (1 < k < n < 3 -105)——分别是两个数组中的元素数量和子段的长度。输入的第二行包含n个整数b1, b2,,bn (1 <b <1012),其中b是数组b的第i个元素。输出打印一个整数-对于从1到n的每个i,满足条件a 2b所需的最小操作数。

Examples

input

Copy

3 3
5 4 6

output

Copy

5

input

Copy

6 3
1 2 3 2 2 3

output

Copy

3

input

Copy

6 3
1 2 4 1 2 3

output

Copy

3

input

Copy

7 3
50 17 81 25 42 39 96

output

Copy

92

题解:
根据贪心的思想每次我们应该找从后往前找b中第一个大于a的,并且把k数组的右边界放到这里

具体证明(不会(毕竟贪心的大部分题目是很难证明其正确性的OuO))

那么我们该怎么模拟这个过程呢,(具体代码中体现:注释)

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#include<set>
using namespace std;
#define int long long
const int N = 4e5 + 10;
typedef pair<int, int> PII;
int n,k;
int b[N];
int a[N];
int add,cnt,ans;
void solve() 
{
	cin >> n >> k;
	for(int i = 1;i <= n;i++)
	{
		cin >> b[i];
	}
	for(int i = n;i >= 1;i --)
	{
		add -= cnt*1;//由于往前一位代表等差数列往前移了一项,长度为k的区间进行了多少次操作(cnt),应该减去cnt*1
		if(i <= n - k)
		cnt -= a[i + k];//由于他是一个长度为k的滑动区间超过这个范围就不影响了,所以把不影响的部分剪掉
		if(add < b[i])
		{
			int t = min(i,k);//区间长度不够k时取最右边的
			int num = (b[i] - add + t - 1)/t;//此时需要多少操作
			a[i] = num;//记录i位置进行了多少次操作
			cnt += num;//长度为k的区间内进行了多少次操作
			ans += num;
			add += num*t;//此时此处经历操作后的大小
		}
	}
	cout << ans;
}

//1 2 4
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
//	cin >> t;
//scanf("%lld",&t);
	while (t--) 
	{
		solve();
	}
}
//1 1 1 0 1

//1 1 1 0 1
//1 1 1 0 1
//1 1 1 0 1
//0 1 1 1 1
//0 1 1 1 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值