CF Round 117C. Optimal Sum 暴力(区间前k大和)+Set

题意:长度为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大和.

随着右端点移动,淘汰掉过时下标,若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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值