题解:CF182C Optimal Sum
题目传送门
题目大意
有一个长度为 \(n\) 的数列 \(a\),可以对它进行最多 \(k\) 次操作,每次操作可以将一个数变为它的相反数,输出最后所有长度为 \(len\) 的数列的和的绝对值的最大值。
思路
根据贪心,我们发现对于一个长度为 \(len\) 的数列,要么把它的前 \(k\) 大正数变为相反数,要么把它的前 \(k\) 小负数变为相反数,这样保证最优。所以我们可以用 \(4\) 个 multiset 分别维护当前在 \(len\) 区间内的前 \(k\) 大正数,区间内的其余正数,区间内的前 \(k\) 小负数,区间内的其余负数。
每次往后移动一位时,判断这一位数的正负,假定为正,那么把他与第一个 multiset 中的最小值比较,如果比它大,那么就把他加入第一个 multiset,否则加入第二个 multiset。否则,加入第二个。
删除同理,如果这个数原本在第一个 multiset 中,那么就把第二个 multiset 中的最大值加入第一个 multiset,否则直接从第二个 multiset 中删除即可。
注意删除前,一定要判断这个数是否在 multiset 中。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010101;
ll n,len,k,a[N],sum1,sum2,sum,ans;
multiset<ll>st1,st2,st3,st4;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>len;
for(int i=1;i<=n;i++)cin>>a[i];
cin>>k;
for(int i=1;i<=n;i++){
sum=sum+a[i]-a[max(i-len,0ll)];
if(k==0){
if(i>=len)ans=max(ans,abs(sum));
continue;
}
if(a[i]>=0){
if(st1.size()<k)st1.insert(-a[i]),sum1+=a[i];
else{
if(-a[i]<=*st1.rbegin()){
sum1+=a[i];
sum1+=*st1.rbegin();
st3.insert(*st1.rbegin());
st1.erase(--st1.end());
st1.insert(-a[i]);
}
else st3.insert(-a[i]);
}
}
if(a[i]<0){
if(st2.size()<k)st2.insert(a[i]),sum2+=a[i];
else{
if(a[i]<=*st2.rbegin()){
sum2+=a[i];
sum2-=*st2.rbegin();
st4.insert(*st2.rbegin());
st2.erase(--st2.end());
st2.insert(a[i]);
}
else st4.insert(a[i]);
}
}
if(a[max(i-len,0ll)]>=0&&i>len){
if(st1.find(-a[i-len])!=st1.end()){
st1.erase(st1.find(-a[i-len]));
st1.insert(*st3.begin());
sum1-=a[i-len];
sum1-=*st3.begin();
if(st3.begin()!=st3.end())st3.erase(st3.begin());
}
else if(st3.find(-a[i-len])!=st3.end())st3.erase(st3.find(-a[i-len]));
}
if(a[max(i-len,0ll)]<0&&i>len){
if(st2.find(a[i-len])!=st2.end()){
st2.erase(st2.find(a[i-len]));
st2.insert(*st4.begin());
sum2-=a[i-len];
sum2+=*st4.begin();
if(st4.begin()!=st4.end())st4.erase(st4.begin());
}
else if(st4.find(a[i-len])!=st4.end())st4.erase(st4.find(a[i-len]));
}
if(i>=len)ans=max(ans,max(abs(sum-2*sum1),abs(sum-2*sum2)));
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号