Stone----HDU_4308----模拟+贪心+一点点二分的思想

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4038

/*
Author:Bob Lee
2012.8.30
========================================
这题是2011成都赛区的网络题,而且号称是里面最水的一道题(但是我还是琢磨来好久)
题目的意思很简单给你n个整数,然后有两种操作让你选择
1.在某一个数上加1
2.插入一个1
然后让你进行m次操作,要你求出m次操作之后最大的乘积

首先,是处理负数的情况。
如果负数是偶数的情况的话,那么就不用考虑这个了。因为相乘的话就变成正数了
如果是奇数的话,那么目前的乘积是个负数,我们则就要想办法消掉一个负数之后,就成正数了
则第一步就是先把绝对值最小的那个负数,也就是最大的那个负数加成0
然后就不用管负数了

接下来我们要考虑的是先把0变成1
因为只要有0存在,乘积就是0,所以第一步就是要把0变成1
接下来就是把1变成0
因为1对于乘积来说没有什么实质的作用,但是如果把1变成2了的话,就相当于翻了一倍
所以这是我们要做的第二步

接下来就是要考虑要不要把2弄成3?
可以发现,2+2+2 = 3+3
但是前面的乘积是8,后面的乘积的话就是9
所以3的乘积的话比2的乘积的话要大
也就是说如果我们将2转化成3,比多加一个2要有用的多
那么接下来要做的就是把所有的2都加成3,当然上面的所有的操作都是建立在操作步骤m足够的情况下

上面三步弄完之后,如果m还有多的话
我们应该怎么处理?

如果还剩1,那么我们显然是把这一个1加到最小的正数上面去,这个是可以自己证明一下的
如果m不为1但是余3的余数为1的话就相当与为4的情况,两种选择3+1 和2+2
显然后面那个的乘积大
则分配m/3-1个3和2个2

如果余数为0的话,则全部分配为3

如果为2的话,则可以分配为m/3个3和1个2

上面就全部分完了
但是这还没有结束

在统计乘法的过程中,由于你加的3或者2可能非常多,所以你一个一个乘进去的话会很费时间
那么我们就利用二分的思想对3和2进行乘积,然后再乘以别的
具体的细节可以在函数cal_3_2里面找到
解决这个之后就可以A掉这个题目了

*/


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;


#define MAXN 100010
#define MOD 1000000007

int data[MAXN];
long long ans,m;
int n;
int cnt;

//这是用来记录插入来多少的3和2的
int three;
int two;

bool cmp(int a,int b)
{
    return abs(a)<abs(b);
}


long long cal_3_2(int kind,int num)
{
    long long b;
    if(num == 0)
        return 1%MOD;
    if(num % 2 == 0)
    {
        long long x = cal_3_2(kind,num/2);
        return x%MOD * x%MOD;
    }
    else
    {
        long long x = cal_3_2(kind,(num-1)/2);
        return  x%MOD * x%MOD * kind%MOD;
    }
}
void read()
{
    cnt = 0;
    three = 0;
    two = 0;
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&data[i]);
        if(data[i] < 0)
            cnt++;
    }
}

void solve()
{
    ans = 1;
    if(cnt & 1) //如果负数为奇数的话,就把最大的负数也就是绝对值最小的负数变成0
    {
        int i;
        sort(data+1,data+1+n,cmp);
        for(i=1;i<=n;i++)
        {
            if(data[i] < 0)
                break;
        }
        if(abs(data[i]) < m)
        {
            m+=data[i];
            data[i] = 0;
        }
        else
        {
            //cout<<"ss"<<endl;
            data[i] = data[i]+m;
            m = 0;
            //cout<<i<<" "<<data[i]<<endl;
            return ;
        }
    }

    //先处理为0的情况
    if(m>0)
    {
        for(int i=1;i<=n && m;i++)
        {
            if(data[i] == 0)
            {
                data[i]++;
                m--;
            }
        }
    }

    //再处理1的情况
    if(m>0)
    {
        for(int i=1;i<=n && m;i++)
        {
            if(data[i] == 1)
            {
                data[i]++;
                m--;
            }
        }
    }

    //再处理2的情况
    if(m>0)
    {
        for(int i=1;i<=n && m;i++)
        {
            if(data[i] == 2)
            {
                data[i]++;
                m--;
            }
        }
    }

    //下面处理m还有多的情况
    if(m>0)
    {
        int loc;
        int max2 = MAXN;

        //找出最小的正数,也许用的到
        for(int i=1;i<=n;i++)
        {
            if(data[i] > 0 && data[i] < max2)
            {
                max2 = data[i];
                loc = i;
            }
        }
        if(m%3 == 0)
        {
            three+=m/3;
            return ;
        }
        else if( m % 3 == 1)
        {
            if(m==1)
            {
                data[loc]++;
                m--;
                return ;
            }
            else//这里非常有意思,因为比如是4的话,你取一个三剩下就是一个2,所以我们的策略就换成取两个2少取一个3
            {
                three+= m/3 - 1;
                two+=2;
                return ;
            }
        }
        else if(m%3==2)
        {
            three += m/3;
            two +=1;
            return ;
        }
    }
}


long long getans()
{
    long long ans2 = 1;
    for(int i=1;i<=n;i++)
    {
        ans2 = ans2*data[i]%MOD;
        //cout<<"ans2 "<<ans2<<endl;
    }

    long long ans_3 = cal_3_2(3,three);
    long long ans_2 = cal_3_2(2,two);
    return ans2*ans_3%MOD*ans_2%MOD;
}

int main()
{
    int t;
    scanf("%d",&t);
    int count = 1;
    while(t--)
    {
        cin>>n>>m;
        read();
        solve();
        ans = getans();
        /*for(int i=1;i<=n;i++)
        {
            cout<<data[i]<<endl;
            cout<<"three "<<three<<endl;
            cout<<"two "<<two<<endl;
        }*/
        cout<<"Case "<<count++<<": "<<ans<<endl;
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值