题意:长度为n的序列a.定义一个区间的价值为:该区间所有的数相加后的绝对值.
操作:改变一个数的正负号.
1<=len<=n<=1e5.0<=k<=n,-1e9<=a[i]<=1e9. 问在操作不超过k次的情况下.长度为len区间的最大价值.
长度为len的区间中,相同符号的显然要尽量多.所以改变的只有该区间前k大正数,或者前k小负数.
如何求所有长度为len区间中,前k大的正数之和? res=max(res,该区间负数和-前k大正数+该区间正数-前k大正数).
用两个set来维护.
第一个set维护长度为len的区间中.被变号的正数.(也就是前k大)
第二个set维护长度为len的区间中.没被选中的正数.
枚举右端点i,当a[i]>0
若size(s1)<k 则把s2中的正数从大到小压入.直到size(s1)==k
现在将a[i]和s1中最小值比较 若能压入.则将mn从s1中弹出,并压入s2(因为后面可能还会用到),更新前k大和.
操作:改变一个数的正负号.
1<=len<=n<=1e5.0<=k<=n,-1e9<=a[i]<=1e9. 问在操作不超过k次的情况下.长度为len区间的最大价值.
长度为len的区间中,相同符号的显然要尽量多.所以改变的只有该区间前k大正数,或者前k小负数.
如何求所有长度为len区间中,前k大的正数之和? res=max(res,该区间负数和-前k大正数+该区间正数-前k大正数).
用两个set来维护.
第一个set维护长度为len的区间中.被变号的正数.(也就是前k大)
第二个set维护长度为len的区间中.没被选中的正数.
枚举右端点i,当a[i]>0
若size(s1)<k 则把s2中的正数从大到小压入.直到size(s1)==k
现在将a[i]和s1中最小值比较 若能压入.则将mn从s1中弹出,并压入s2(因为后面可能还会用到),更新前k大和.
随着右端点移动,淘汰掉过时下标,若i>=len 把a[i-len+1]在s1或者s2中弹出即可.
size(s1)>=k时 最多弹出和压入一个元素,
size(s1)<k 要么压入还没进过s1的元素.要么是从s1中被淘汰的元素.后者最多n个.所以O(nlogn)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const int N=2e5+5;
ll n,k,len,a[N],pc[N],pb[N],sumb=0,sumc=0;
set<ii> b,bb,c,cc;
bool mb[N],mc[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>len;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>k;
for(int i=1;i<=n;i++)
{
pc[i]=pc[i-1];
pb[i]=pb[i-1];
if(a[i]>0) pb[i]+=a[i];
else pc[i]+=a[i];
}
ll res=0;
if(k==0){
for(int i=len;i<=n;i++) res=max(res,abs(pb[i]+pc[i]-pc[i-len]-pb[i-len]));
cout<<res<<'\n';
return 0;
}
for(int i=1;i<=n;i++)
{
while(b.size()<k&&bb.size()){
ll val=(*bb.rbegin()).first,idx=(*bb.rbegin()).second;
bb.erase(ii(val,idx));
b.insert(ii(val,idx));
sumb+=val;
mb[idx]=true;
}
if(a[i]>0)
{
if(b.size()<k) b.insert(ii(a[i],i)),sumb+=a[i],mb[i]=true;
else
{
ll val=(*b.begin()).first,id=(*b.begin()).second;
if(val<a[i]){
b.erase(ii(val,id));
bb.insert(ii(val,id));
b.insert(ii(a[i],i));
sumb=sumb-val+a[i];
mb[i]=true,mb[id]=false;
}
else bb.insert(ii(a[i],i));//a[i]还没加入集合.并且为len区间内正数.
}
}
if(i>=len)
{
res=max(res,abs((pc[i]-pc[i-len]-sumb)+(pb[i]-pb[i-len]-sumb)));
int idx=i-len+1;
if(mb[idx]){
mb[idx]=false;
b.erase(ii(a[idx],idx));
sumb-=a[idx];
}
else if(a[idx]>0)
bb.erase(ii(a[idx],idx));
}
}
for(int i=1;i<=n;i++)
{
while(c.size()<k&&cc.size()){
ll val=(*cc.begin()).first,idx=(*cc.begin()).second;
cc.erase(ii(val,idx));
c.insert(ii(val,idx));
sumc+=val;
mc[idx]=true;
}
if(a[i]<0)
{
if(c.size()<k) c.insert(ii(a[i],i)),sumc+=a[i],mc[i]=true;
else
{
ll val=(*c.rbegin()).first,id=(*c.rbegin()).second;
if(a[i]<val){
c.erase(ii(val,id));
cc.insert(ii(val,id));
c.insert(ii(a[i],i));
sumc=sumc-val+a[i];
mc[i]=true;
mc[id]=false;
}
else
cc.insert(ii(a[i],i));//a[i]还没加入集合.并且为len区间内为负数.
}
}
if(i>=len)
{
res=max(res,abs((pb[i]-pb[i-len]-sumc)+(pc[i]-pc[i-len]-sumc)));
int idx=i-len+1;
if(mc[idx]){
mc[idx]=false;
c.erase(ii(a[idx],idx));
sumc-=a[idx];
}
else if(a[idx]<0)
cc.erase(ii(a[idx],idx));
}
}
cout<<res<<'\n';
return 0;
}
大佬の代码:
#include <set>
#include <iostream>
using namespace std;
struct Set {
int c;
long long slo, shi;
multiset<long long> hi;
multiset<long long, greater<long long> > lo;
void init(int n) {
c = n;
slo = shi = 0LL;
}
void insert(long long x) {
hi.insert(x);
shi += x;
if ((int)hi.size() > c) {
x = *hi.begin();
hi.erase(hi.begin());
lo.insert(x);
shi -= x;
slo += x;
}
}
void erase(long long x) {
if (lo.count(x) > 0) {
lo.erase(lo.find(x));
slo -= x;
} else {
hi.erase(hi.find(x));
shi -= x;
if (!lo.empty()) {
x = *lo.begin();
lo.erase(lo.begin());
hi.insert(x);
slo -= x;
shi += x;
}
}
}
} pos, neg;
const int MAXN = 100100;
long long a[MAXN];
void insert(long long x) {
if (x >= 0) {
pos.insert(x);
} else {
neg.insert(-x);
}
}
void erase(long long x) {
if (x >= 0) {
pos.erase(x);
} else {
neg.erase(-x);
}
}
long long query() {
return max(
pos.shi + pos.slo + neg.shi - neg.slo,
neg.shi + neg.slo + pos.shi - pos.slo);
}
int main() {
int n, m, k;
long long ans = 0;
cin >> n >> m;
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
cin >> k;
pos.init(k);
neg.init(k);
for (int i = 0; i < n; ++i) {
if (i >= m) {
erase(a[i - m]);
}
insert(a[i]);
if (i >= m) {
ans = max(ans, query());
} else if (i == m - 1) {
ans = query();
}
}
cout << ans << endl;
return 0;
}