Codeforces Round #117 (Div. 2) C. Optimal Sum

http://www.codeforces.com/contest/182/problem/C

题意:给你一组有N个数的数列,给你最多K次将其中的一个数取反 的机会,即原来是a的数,经过这种操作之后,这个数会变成-a。问在最多允许k次这种操作的情况下,最大的连续len个数的sum是多少。N<=100000

思路:这是一道3000分的题,比赛的时候没做出来,是比了赛之后看别人的代码之后才想明白的。题意很容易理解,只是在len中将取反的名额分给谁是一个需要我们决策的问题,我们可以这样想,如果这len个中,负数的个数小于k个的话,我们可以直接将这几个负数全部取反,这样就可以达到最大sum;现在考虑len区间内的负数的个数大于k个,这就要求我们选择绝对值最大的k个将负数取反,这样的和也是最优的,这样的话我们就把问题转换成了在len个元素中选出绝对值前k大的值,这个操作我们可以用一个优先队列实现,复杂度为:O(logn)。接下去我们将区间先后移动一步,这样就会将一个元素移出,一个元素移入,移出的元素我们需要判断是否是取反的元素,因此我们还需要用一个数组标记每个负数是否取反了。考虑移入区间的新元素,如果是正数,则加上;如果是负数,就要考虑是否取反,这里当然也是要考虑将现在len区间内绝对值前k个大的元素取反,一开始这个地方没有考虑到, WA了。。 

代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
typedef __int64  LL ;
const int MAXN = 100100;
const LL inf = 1000000000000000ll ;
LL n , len ,k ;
LL a[MAXN] ;
priority_queue< pair<LL,LL> ,vector< pair<LL,LL> > , greater< pair<LL,LL> > > que ;
priority_queue< pair<LL,LL> ,vector< pair<LL,LL> > , less< pair<LL,LL> > > que1;

bool vis[MAXN] ;

void solve(){
	LL i ,j ,f , c ,v ,b;
	LL ans = -inf ;
	for(f=0;f<2;f++){		//最终的最大sum是正数还是负数
		if(f == 1){
			for(i=1;i<=n;i++){
				a[i] = -a[i] ;
			}
		}
		c = 0 ;
		LL res = 0 ;
		while(!que.empty()) que.pop() ;
		memset(vis, 0 ,sizeof(vis));
		while(!que1.empty()) que1.pop() ;
		
		for(i=1;i<=n;i++){			//计算在最多k次操作的情况下,第一个len区间的最大和
			if( a[i]<0 ){
				if( c==k ){
					if( !que.empty() && -a[i]>que.top().first ){
						v = que.top().first ;		//val 
						b = que.top().second ;		//number
						que1.push( make_pair(v,b) );
						vis[b] = 0 ;
						que.pop() ;	
						que.push( make_pair( -a[i] , i) ) ;		
						vis[i] = 1 ;
						res -= 2*v ;
						res -= a[i] ;
					}
					else{
						res += a[i] ;
						que1.push( make_pair( -a[i] , i) );
					}
				}
				else{
					que.push( make_pair(-a[i] , i));
					vis[i] = 1 ;
					c++ ;
					res -= a[i] ;
				}
			}	
			else
				res += a[i] ;
			if(i == len)	break ;
		}
		if(ans < res)	ans = res ;
		
		for(i++ ;i<=n;i++){			//移动区间
			if( vis[i-len] == 1){
				res += a[i-len] ;
				c -- ;
			}
			else{
				res -= a[i-len] ;
			}
			while(!que1.empty() && c!=k){
				v = que1.top().first ;
				b = que1.top().second ;
				if(b <= i-len)	que1.pop() ;
				else{
					res += v*2 ;
					c++ ;		
					que1.pop() ;
					que.push( make_pair(v,b) );
					vis[b] = 1 ;
				}
			}
			if( a[i] < 0 ){
				if( c==k ){
					while(!que.empty() && que.top().second <= i-len ){
				 		que.pop() ;
					}
					if(!que.empty() && que.top().first < -a[i]){
						v = que.top().first ;
						b = que.top().second ;
						vis[ b ] =  0;
						que.pop() ;
						que1.push( make_pair(v,b) ) ;
						que.push( make_pair(-a[i] , i)) ;
						vis[i] = 1;
						res -= 2 * v ;
						res -= a[i] ;
					}
					else{
						res += a[i] ;
						que1.push( make_pair( -a[i] , i) );
					}
				}
				else{
					que.push( make_pair(-a[i] , i) ) ;
					vis[i] = 1;
					res -= a[i] ;
					c++ ;
				}
			}
			else{
				res += a[i] ;
			}	
			if( ans < res )	ans = res ;
		}
	}
	printf("%I64d\n",ans);
}	

int main(){
	while(scanf("%I64d%I64d",&n,&len) == 2){
		for(int i=1;i<=n;i++){
			scanf("%I64d",&a[i]);
		}			
		scanf("%I64d",&k);
		solve() ;
	}
	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值