JZOJ7月17日提高组T2 数组
题目
Description
Input
Output
Sample Input
输入样例1:
3 2 7
5 4 2
输入样例2:
5 3 1
5 4 3 5 5
Sample Output
输出样例1:
999999732
输出样例2:
0
Data Constraint
题解
题意
给定一个序列,可以选择一些元素进行最多 次操作,每次操作可以对序列内一个元素增加或减少给定的数 ,问这个序列的最小乘积是多少,对 取模
分析
既然要乘积最小,那么尽量使乘积为负数
那么就要使序列内负数的个数有奇数个
如果当前序列内负数个数为偶数个,有两种方法:
1、将一个负数增加至非负数
2、将一个非负数减少至负数
可以取负数和非负数里绝对值最小的两个数,判断是减还是加
如果序列负数为奇数时(包括上述更改后)
把所有数全部变成绝对值
然后将最小的数加上
为什么是最小的数呢?
设未更改前序列乘积为
,要把当前值为
的数加上
,那么更改后的乘积就为
显而易见,
越小式子总值越大
那么就可以用堆来维护最小值
最后输出时判一下正负(或许不用)
Code
#include<cstdio>
#include<cmath>
#define mod 1000000007
using namespace std;
int n,m,v,i,nmax,pmin,nnum,pnum,f,num,a[200005];
long long d[400005],ans;
bool b;
void up(int x)
{
long long t;
while (x>1&&d[x/2]>d[x])
{
t=d[x/2];
d[x/2]=d[x];
d[x]=t;
x/=2;
}
}
void down(int x)
{
int y;
long long t;
while ((x*2<=num&&d[x]>d[x*2])||(x*2+1<=num+1&&d[x]>=d[x*2+1]))
{
y=x*2;
if (y+1<=num&&d[y+1]<d[y]) y++;
t=d[x];
d[x]=d[y];
d[y]=t;
x=y;
}
}
int main()
{
nmax=-1234567890;
pmin=1234567890;
scanf("%d%d%d",&n,&m,&v);
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]<0) f++;
if (a[i]<0&&a[i]>nmax)
{
nmax=a[i];
nnum=i;
}
if (a[i]>=0&&a[i]<pmin)
{
pmin=a[i];
pnum=i;
}
}
if (f%2==0)
{
if (abs(nmax)>pmin)
{
while (a[pnum]>0&&m>0)
{
m--;
a[pnum]-=v;
}
if (a[pnum]<0) b=true;
}
else
{
while (a[nnum]<0&&m>0)
{
m--;
a[nnum]+=v;
}
if (a[nnum]>0) b=true;
}
}
else b=true;
ans=1;
if (m>0)
{
for (i=1;i<=n;i++)
{
a[i]=abs(a[i]);
num++;
d[num]=a[i];
up(num);
}
while (m--)
{
d[1]+=v;
down(1);
}
for (i=1;i<=n;i++)
ans=ans*(d[i]%mod)%mod;
printf("%d\n",(mod-ans)%mod);
}
else
{
for (i=1;i<=n;i++)
ans=ans*(abs(a[i])%mod)%mod;
if (b==true) printf("%d\n",(mod-ans)%mod);
else printf("%d\n",ans);
}
return 0;
}