Codeforces 721D Maxim and Array【贪心+优先队列+分类讨论】

186 篇文章 0 订阅
43 篇文章 0 订阅

D. Maxim and Array
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Recently Maxim has found an array of n integers, needed by no one. He immediately come up with idea of changing it: he invented positive integer x and decided to add or subtract it from arbitrary array elements. Formally, by applying single operation Maxim chooses integer i (1 ≤ i ≤ n) and replaces the i-th element of array ai either with ai + x or with ai - x. Please note that the operation may be applied more than once to the same position.

Maxim is a curious minimalis, thus he wants to know what is the minimum value that the product of all array elements (i.e. ) can reach, if Maxim would apply no more than k operations to it. Please help him in that.

Input

The first line of the input contains three integers n, k and x (1 ≤ n, k ≤ 200 000, 1 ≤ x ≤ 109) — the number of elements in the array, the maximum number of operations and the number invented by Maxim, respectively.

The second line contains n integers a1, a2, ..., an () — the elements of the array found by Maxim.

Output

Print n integers b1, b2, ..., bn in the only line — the array elements after applying no more than k operations to the array. In particular, should stay true for every 1 ≤ i ≤ n, but the product of all array elements should be minimum possible.

If there are multiple answers, print any of them.

Examples
Input
5 3 1
5 4 3 5 2
Output
5 4 3 5 -1 
Input
5 3 1
5 4 3 5 5
Output
5 4 0 5 5 
Input
5 3 1
5 4 4 5 5
Output
5 1 4 5 5 
Input
3 2 7
5 4 2
Output
5 11 -5 

题目大意:

一共给你N个数,可以进行k次操作,每次操作可以选择数列中任意一个数,使得其+x或者是-x.

目标是为了使得最终数列的乘积最小,问怎样处理能够达到目的。


思路:


1、假设我们此时有奇数个负数,也就是说此时的序列的乘积是负数的,那么显然我们希望结果最小,那么只要使得这个乘积更小即可。

假设我们此时将序列按照绝对值之后的值从小到大排序之后,我们改变哪个值可以使得乘积减小的贡献值更大呢?显然是对于a【0】的处理,我们将绝对值最小的数,如果他是正数 ,那么增大他,否则减小他,显然可以使得乘积减小的贡献值最大化。

那么对于这部分的处理,我们维护一个优先队列处理K次即可。


2、那么我们接下来分类讨论原序列:

设定cnt表示原序列负数的个数。

①cnt==0.那么就是说原序列的值都是>=0的,那么我们此时找到最小的a【i】.尽可能的将其变成负数,变成负数之后,进入上述有奇数个数负数的处理阶段。如果操作完了也没有将其变成负数,那么输出最终序列即可。

②cnt>0&&cnt%2==1,就是说原序列已经有奇数个负数了,那么直接进入上述奇数个数负数的处理阶段。

③cnt>0&&cnt%2==0,那么我们希望将某一个正数变成负数,或者某一个负数变成整数,以形成奇数个负数的情况,再进入上述奇数个数负数的处理阶段即可。

那么对于这种情况,我们找到绝对值最小的a【i】,如果是正数 ,将其变成负数,否则将其变成正数。


3、注意数据范围,需要使用LL...........


Ac代码(略挫....):

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll __int64
struct node
{
    ll pos,val;
    friend bool operator <(node a,node b)
    {
        return abs(a.val)>abs(b.val);
    }
} nod;
ll a[200050];
ll ans[200050];
ll n,m,k;
void Slove()
{
    priority_queue<node>s;
    for(ll i=0; i<n; i++)
    {
        nod.pos=i;
        nod.val=a[i];
        s.push(nod);
    }
    while(m--)
    {
        nod=s.top();
        s.pop();
        if(nod.val<0)
        {
            nod.val-=k;
        }
        else nod.val+=k;
        s.push(nod);
    }
    while(!s.empty())
    {
        nod=s.top();
        s.pop();
        ans[nod.pos]=nod.val;
    }
    for(ll i=0; i<n; i++)
    {
        printf("%I64d ",ans[i]);
    }
    printf("\n");
}
int main()
{
    while(~scanf("%I64d%I64d%I64d",&n,&m,&k))
    {
        ll cnt=0;
        for(ll i=0; i<n; i++)
        {
            scanf("%I64d",&a[i]);
            if(a[i]<0)cnt++;
        }
        if(cnt==0)
        {
            ll minn=0x3f3f3f3f,pos;
            for(ll i=0;i<n;i++)
            {
                if(a[i]<minn)
                {
                    minn=a[i];pos=i;
                }
            }
            while(m--)
            {
                a[pos]-=k;
                if(a[pos]<0)break;
            }
            if(a[pos]<0)Slove();
            else
            {
                for(ll i=0;i<n;i++)
                {
                    printf("%I64d ",a[i]);
                }
                printf("\n");
            }
        }
        else if(cnt%2==1)
        {
            Slove();
        }
        else
        {
            ll minn=0x3f3f3f3f;
            ll pos;
            ll zhengfu;
            for(ll i=0;i<n;i++)
            {
                if(abs(a[i])<minn)
                {
                    minn=abs(a[i]);
                    pos=i;
                    if(a[i]<0)zhengfu=-1;
                    else zhengfu=1;
                }
            }
            ll ok=0;
            while(m--)
            {
                if(a[pos]<0)a[pos]+=k;
                else a[pos]-=k;
                if(zhengfu==1&&a[pos]<0)
                {
                    ok=1;
                    break;
                }
                if(zhengfu==-1&&a[pos]>=0)
                {
                    ok=1;
                    break;
                }
            }
            if(ok==1)Slove();
            else
            {
                for(ll i=0;i<n;i++)
                {
                    printf("%I64d ",a[i]);
                }
                printf("\n");
            }
        }
    }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值