题目链接:Click here~~
题意:
给一个数列{ai},有两种操作:1、选取数列中的一个数并给它加1。
2、在数列中添加一个数字1。
问如何进行 m 次操作,使数列各项乘积 p 的值最大。
解题思路:
由于数列中有正负数和0,所以导致情况很多,让我们逐个来考虑。
1、负数个数为奇数。(p<=0)
如果数列中不存在0,那么乘积 p 一定为负。不难想到,最好的方法是选绝对值最小的那个负数,把它加到0,使 p 变成 0。
此时进入情况2。
2、数列中含有0。(p=0)
要想把p上升成正数,显然需要把所有的0变成1。
然后进入情况3。
3、负数个数为偶数。(p>0)
首先,肯定不动负数,因为给负数加1,会使其绝对值减小,即 p 会减小。
而且,如果进行操作1,我选的那个数一定是当前数列中最小的正数,显然这样会使 p 变得更大。
如果进行操作2,在不是毫无办法的情况下,我绝对不会傻傻的只添加个1,因为那样对 p 没有贡献。
也就是说,我会一直把它添加到x(x>=2),即操作2一定会和操作1结合起来使用,为了方便,我们把这些结合起来的操作都看做是操作2。
另外,我不会选择进行操作2后再去进行操作1。因为操作2新添加的数,不会超过之前数列中最小的正数。
下面,对于那些正数,比较操作1 和操作2 哪个更划算。
首先,当数列中含有1的时候,一定选操作1。因为把1个1变成2,只需要消耗1步操作就可以达到和消耗2步操作2 相同的效果。
于是我们考虑不含1的数列,比如{a,b,c,d}(按从小到大的顺序排列,a>=2),操作步数为 m。
当m=1 时:操作 1 更佳。
当m=2 时:①a=b。△1 = (2b+1)*cd,△2 = b^2*cd。令△2 < △1,得 b <= 2。又a=b,得唯一解 a = b = 2。
②a<b。△1 = 2b*cd,△2 = a*b*cd。由于a >= 2,所以△2 >= △1。且当 a=2 时,等号成立。
综上,当 a=2 时,操作1 更佳,否则 操作2 更佳。
推论:把所有的2变成3不会使结果更差。
当m>2时:不会证明了。。。囧。。。
最后还剩下的操作就靠下面这个结论了:
——————————————————————————————分割线——————————————————————————————
Q:将一个正整数 x 分解成 k 个正数 ai 相加的形式,问如何才能使得到的各个 ai 的乘积 S 最大(i:1~k , k任意)。
结论1:ai > 1。
如果ai = 1,显然它对乘积 S 没有贡献,不如把它随便加到其他的一个数上,得到的结果一定更大。
结论2:ai < 4。
第一种证明方法:
反证法,假设有某个ai>=4,那么如果把 ai 分成 2+(ai-2) 的形式,得到的乘积 2*ai-4 = ai + (ai-4) >= ai,肯定不会使结果更差。
第二种证明方法:
设任意正整数a、b,考虑什么时候分解会使结果更大,令 a*b >= a+b,即 (a-1)*(b-1) >= 1,可推出 a>=2,b>=2,且当 a=b=2 时等号成立。
所以如果有 ai>=4,那么一定可以找到那样的整数a、b,使得 a*b >= a+b,也不会使结果更差。
结论3:ai 中 2 的个数不超过 2 个。
反证法,假设 2 的个数大于等于3个,那么 3个2 不如 2个3 得到的乘积大。
综上,若 x = 3*k ,ans = 3^k;
若 x = 3*k + 1,ans = 3^(k-1) * 2*2;
若 x = 3*k + 2,ans = 3^k * 2。
——————————————————————————————分割线——————————————————————————————
#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef __int64 LL;
const int mod = 1000000007;
int mypow(int a,LL n)
{
int ans = 1;
for(LL t=a;n;n>>=1)
{
if(n&1)
ans = (ans*t) % mod;
t = (t*t) % mod;
}
return ans;
}
int main()
{
// freopen("in.ads","r",stdin);
// freopen("ans.ads","w",stdout);
int z,n,x,ncase = 0;
vector<int> NV,PV;
scanf("%d",&z);
LL m,cnt[4];
while(z--)
{
memset(cnt,0,sizeof(cnt));
scanf("%d%I64d",&n,&m);
bool neg = false;
int minPos = 10001,pid;
int maxNeg =-10001,nid;
NV.clear();
PV.clear();
while(n--)
{
scanf("%d",&x);
if(x >= 0)
{
if(x < 4)
{
++cnt[x];
continue;
}
PV.push_back(x);
if(x < minPos)
minPos = x , pid = PV.size()-1;
}
else
{
NV.push_back(x);
if(x > maxNeg)
maxNeg = x , nid = NV.size()-1;
neg ^= 1;
}
}
if(neg) //把最大的负数加成0
{
if(NV[nid]+m >= 0)
{
m += NV[nid];
cnt[0]++;
NV.erase(NV.begin()+nid);
}
else
{
NV[nid] += m;
m = 0;
}
}
if(m >= cnt[0]) //把0加成1
{
m -= cnt[0];
cnt[1] += cnt[0];
cnt[0] = 0;
if(m <= cnt[1]) //把1加成2
{
cnt[2] += m;
}
else
{
m -= cnt[1];
cnt[2] += cnt[1];
if(m <= cnt[2]) //把2加成3
{
cnt[2] -= m;
cnt[3] += m;
}
else
{
m -= cnt[2];
cnt[3] += cnt[2];
cnt[2] = 0;
if(m == 1)
{
if(cnt[3])
cnt[3]-- , PV.push_back(4);
else
if(!PV.empty())
PV[pid]++;
}
else
{
if(m%3 == 0)
{
cnt[3] += m/3;
}
else if(m%3 == 1)
{
cnt[3] += m/3-1;
cnt[2] += 2;
}
else
{
cnt[3] += m/3;
cnt[2] += 1;
}
}
}
}
}
LL ans = 1;
if(!cnt[0])
{
for(int i=2;i<=3;i++)
ans = (ans*mypow(i,cnt[i])) % mod;
for(int i=0;i<NV.size();i++)
ans = ans*NV[i] % mod;
for(int i=0;i<PV.size();i++)
ans = ans*PV[i] % mod;
}
printf("Case %d: %I64d\n",++ncase,cnt[0] ? 0 : ans);
}
return 0;
}