题目地址: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;
}