srupのメモ帳

競プロで解いた問題や勉強したことを記録していくメモ帳

codeforces 374 div2 D. Maxim and Array

問題

問題概要

数列aが与えられる。この数列の積を最小に知るために、k回数列の数字を+xまたは、-xすることができる。どのように数列を変えるかを求めよ。

解法

重要な考察が、数列のすべての数字の積を大きくするには、一番小さい数字を大きくしていけばいいということと、積がマイナスになることができるなら、マイナスにするほうがいいということ。
実装は、まず、与えらた数列に負の数がいくつあるかを求め、その数が奇数であれば、すでに積がマイナスなので、あとは、k回分数列の中で、絶対値が一番小さい数を大きくするように処理すればいい。絶対値が一番小さいやつを求めるのにはpriority_queueを用いればよい。また、偶数の場合は、一番絶対値が小さい数を1つ、符号を変える。符号を変えれる場合(abs(mi) < k * x)は、符号を変えて、残りの回数だけ、もともと積が負の場合と同じ処理をすればいい。絶対値が一番小さい数字でさえ符号を変えられない場合は、絶対値が一番小さい数字の絶対値を小さくするように、その数からk*xを足すか引けばよい。これは、積を大きくするには、一番小さい数字を大きくしていくのだから、逆に考えると、積を小さくしていくには、一番小さい数字を小さくしていけばよい。

ミス

考察が厳しいね。

コード

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<ll, int> P;
#define rep(i,n) for(int i=0;i<(n);i++)
const ll INF = 1e12;

int n, k;
ll x;
ll a[200010];

void absmax(void){
    priority_queue<P, vector<P>, greater<P> > q;
    rep(i, n){
        q.push(make_pair(abs(a[i]), i));
    }
    //絶対値が小さいものから優先的に大きくしていく
    rep(i, k){
        auto tmp = q.top(); q.pop();
        int idx = tmp.second;
        if(a[idx] >= 0){
            a[idx] += x;
            q.push(make_pair(abs(a[idx]), idx));
        }else{
            a[idx] -= x;
            q.push(make_pair(abs(a[idx]), idx));
        }
    }
    return;
}

int main(void){
    cin >> n >> k >> x;
    ll sign = 0;
    ll mi = INF;
    int miidx;
    rep(i, n){
        cin >> a[i];
        if(a[i] < 0) sign++;
        if(abs(a[i]) < abs(mi)){
            mi = a[i];
            miidx = i;
        }
    }
    sign %= 2;

    if(sign == 1){//現在積は負
        absmax();
    }else{//現在積は正
        if(abs(mi) < k * x){//一番小さいものの符号を変えられる
            //絶対値が一番小さいものの符号を変える
            if(mi >= 0){
                while(a[miidx] >= 0){
                    a[miidx] -= x;
                    k--;
                }
                absmax();
            }else{
                while(a[miidx] <= 0){
                    a[miidx] += x;
                    k--;
                }
                //符号を変えて全部の積でマイナスになったら、次は絶対値で一番小さいものの絶対値を小さくしていけばいい
                absmax();
            }
        }else{//一番小さいものの符号を変えられない
            //絶対値が一番小さいものの絶対値を小さくすればいい
            if(mi >= 0){
                a[miidx] -= k * x;
            }else{
                a[miidx] += k * x;
            }
        }
    }

    rep(i, n){
        printf("%lld ", a[i]);
    }
    printf("\n");
}