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

来源:互联网 发布:jdk 7u80 windows x32 编辑:程序博客网 时间:2024/05/23 02:03

题目: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