题目大意
给你一个长度为 n n n的序列 a a a,和整数 m m m和 k k k。对于每个 i = 1 , 2 , … n − m + 1 i=1,2,\dots n-m+1 i=1,2,…n−m+1,求出 a i , a i + 1 … a i + m − 1 a_i,a_i+1\dots a_{i+m-1} ai,ai+1…ai+m−1按升序排序后前 k k k个数的和。
令集合 L L L存储 a i , a i + 1 … a i + m − 1 a_i,a_{i+1}\dots a_{i+m-1} ai,ai+1…ai+m−1中前 k k k小的值,集合 R R R存储这 m m m个数中除了在 L L L中的值的其他的值。那么显然满足 max L < min R \max L<\min R maxL<minR,且此时 L L L中各元素之和就是当前的答案。
对于每一次 i i i的增加, a i a_i ai被删除, a i + m a_{i+m} ai+m被加入。我们可以做以下处理:
- 确定 a i a_i ai在集合 L L L还是集合 R R R,删除 a i a_i ai
- 如果 a i + m < max L a_{i+m}<\max L ai+m<maxL,则放入集合 L L L;否则放入集合 R R R
此时 L L L满足 k − 1 ≤ ∣ L ∣ ≤ k + 1 k-1\leq|L|\leq k+1 k−1≤∣L∣≤k+1。我们要让 L L L的长度变为 k k k,则如果 ∣ L ∣ = = k − 1 |L|==k-1 ∣L∣==k−1,将 R R R中最小的元素放入 L L L中;如果 ∣ L ∣ = = k + 1 |L|==k+1 ∣L∣==k+1,则将 L L L中最大的元素放入 R R R中。即可保证 L L L的长度始终为 k k k,即可求出当前答案。
用 m u l t i s e t multiset multiset可以使每次插入和删除操作为 O ( log n ) O(\log n) O(logn),那么总时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
long long now=0,a[200005],ans[200005];
multiset<int>s;
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
s.insert(a[i]);
}
multiset<int>::iterator it=s.begin();
for(int i=1;i<=k;i++,it++) now+=*it;
--it;
for(int i=1;i<=n-m;i++){
ans[i]=now;
s.insert(a[i+m]);
if(a[i+m]<*it){
now=now-*it+a[i+m];--it;
}
if(a[i]<=*it){
++it;now=now-a[i]+*it;
}
s.erase(a[i]);
}
ans[n-m+1]=now;
for(int i=1;i<=n-m+1;i++){
printf("%lld ",ans[i]);
}
return 0;
}
本文介绍了解决ABC281E问题的方法,通过维护两个集合L和R来求解给定序列中特定子序列的前k小元素之和。使用multiset实现高效的插入和删除操作。
168万+

被折叠的 条评论
为什么被折叠?



