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;
}