Codeforces 721D (优先队列,模拟)

题目:http://codeforces.com/contest/721/problem/D
题意:

给出一个n个数的序列,最多可以有k次操作,每次操作可以任选序列中的一个数+x或者-x,求如何使得n个数的积最小?

分析:

这题比较麻烦,有一些情况
首先分析,要想积最小,那么就把积变成负数。
如果可以变成负数,那么就尽量让所有数的绝对值平均一些,这样积的绝对值才更大。
怎么变负数呢?
首先找出序列中的0,首先要把0变一下,如果0的个数大于k,那么积肯定是0,这样的话怎么变无所谓了。
如果负数的个数是奇数,那么就不需要把其他的数变成负数了,这样把0都+x
如果负数的个数是偶数,那么把一个0变成负数(-x),其他的+x
如果负数的个数还是偶数,那么就找一个绝对值最小的数,如果这个数是负数,那么变成正数;否则,变成负数。
如果有了负数以后,接下来就是找序列中绝对值最小的那个数,根据正负+或-x。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+9;
struct num {
    ll abs,val,idx;
    num(ll val,ll abs,ll idx):val(val),abs(abs),idx(idx) {}
    bool operator < (const num& rhs)const {
        return abs>rhs.abs;
    }
};
ll a[N],n,k,x,zero,neg;
int main() {
    //freopen("f.txt","r",stdin);
    priority_queue<num>q;
    scanf("%I64d%I64d%I64d",&n,&k,&x);
    zero=neg=0;
    for(int i=1; i<=n; i++) {
        scanf("%I64d",&a[i]);
        if(a[i]==0)zero++;
        if(a[i]<0)neg++;
        q.push(num(a[i],abs(a[i]),i));
    }
    if(zero>k) {
        for(int i=1; i<=n; i++)printf("%I64d ",a[i]);
        return 0;
    }

    if(neg%2==0) {
        if(zero) {
            neg++;
            zero--,k--;
            num t=q.top();
            q.pop();
            a[t.idx]-=x;
            t.val-=x;
            t.abs+=x;
            q.push(t);
        }
    }
    while(zero--) {
        k--;
        num t=q.top();
        q.pop();
        a[t.idx]+=x;
        t.val+=x;
        t.abs+=x;
        q.push(t);
    }
    if(neg%2==0) {
        num t=q.top();
        q.pop();
        if(t.val<0) {
            while(t.val<=0&&k)t.val+=x,k--;

        } else
            while(t.val>=0&&k)t.val-=x,k--;
        a[t.idx]=t.val;
        t.abs=abs(t.val);
        q.push(t);
    }
    while(k--) {
        num t=q.top();
        q.pop();
        if(t.val<0)a[t.idx]-=x;
        else a[t.idx]+=x;
        t.abs+=x;
        q.push(t);
    }
    for(int i=1; i<=n; i++)printf("%I64d ",a[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值