ABC281 E - Least Elements

E - Least Elements

题目大意:给你一个整数数组 A A A,求每段长为 M M M的数列中前 K K K小的数的和

思路

暴力 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)显然不可行,发现可以参考单调队列求区间最值的思路。

首先从左到右 O ( N ) O(N) O(N)遍历,每次删除最左的,插入最右的,并记录答案。
因为有删除,插入,还要从小到大,因此用数组,队列都不好实现,选择使用set。

数列是可重元素的,改用可重集。

对于每次插入,仅当插入值小于当前数列最大值才会产生贡献。
对于每次删除,仅当删除值小于等于当前数列最大值才会产生贡献。

最终代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k,a[200005];
long long t;
multiset<int> s;
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++) scanf("%d",a+i),s.insert(a[i]);
    multiset<int>::iterator it=s.begin(),it2;
    for(int i=1;i<=k;i++) t+=*it,it++;it--;
    printf("%lld ",t);
    for(int i=m+1;i<=n;i++){
        scanf("%d",a+i);s.insert(a[i]);
        if(a[i]<*it) t+=a[i]-*it,it--;
        if(a[i-m]<*it) it++,t+=*it-a[i-m],s.erase(a[i-m]);
        else if(a[i-m]==*it){
            it2=it;it++;
            t+=*it-a[i-m];
            s.erase(it2);
        }
        else s.erase(a[i-m]);
        printf("%lld ",t);
    }
}

Code 短但有Bug

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,k,a[200005];
LL tot;
multiset<int> s;
int main(){
    scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++){
        scanf("%d",a+i);
		s.insert(a[i]);
	}
	multiset<int>::iterator it=s.begin();
	for(int i=0;i<k;i++) tot+=*it,it++;
	it--;
    printf("%lld ",tot);
	for(int i=m+1;i<=n;i++){
		scanf("%d",a+i);
		s.insert(a[i]);
		if(a[i]<*it) tot+=a[i]-*it,it--;
		if(a[i-m]<=*it) it++,tot+=*it-a[i-m];
		s.erase(a[i-m]);
        printf("%lld ",tot);
	}
    return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值