题目大意:
给你一个长为$n$的数组和$m$次操作机会,每次操作可以选择一个数使其加上或减去$k$,请你求出进行m次操作后得到序列的乘积最小值.
solution:
此题很明显是贪心,需要努力将所有数的乘积变为负,故消$0$策略:如果当前乘积已为负,则全部变正,否则使最后一个$0$变为负即可.
若没有$0$可转乘积符号且当前乘积为正,则需找到最接近$0$的数字并努力改变其符号,若操作次数不足以改变符号,则直接出答案.
$0$全部消去且乘积为负时,需用一个小根堆维护当前序列,堆的键值为序列中每个数的绝对值,然后每次操作将堆顶远离$0$即可.
code:
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #include<algorithm> using namespace std; long long n,k,x; long long mod=1000000007; long long a[1000010]; priority_queue<pair<long long,long long> > q; int main(){ scanf("%ld%ld%ld",&n,&k,&x); for(long i=1;i<=n;i++) scanf("%ld",&a[i]); sort(a+1,a+n+1); long long cnt=0; long long rt=0; for(long i=1;i<=n;i++) if(a[i]<0) rt++; else break; if(rt%2==0) rt=2; else rt=1; long long y,z; for(long i=1;i<=n;i++) a[i]=abs(a[i]); sort(a+1,a+n+1); long sum=1; if(x==0 && rt==1) { for(long i=1;i<=n;i++) sum*=a[i],sum%=mod; sum=-sum; sum=((sum%mod)+mod)%mod; } if(x==0 && rt==2) { for(long i=1;i<=n;i++) sum*=a[i],sum%=mod; sum=((sum%mod)+mod)%mod; } if(a[1]-x*k<=0 && rt==2){ while(a[1]>0) { a[1]=a[1]-x,cnt++;} k=k-cnt; a[1]=abs(a[1]); for(long i=1;i<=n;i++) q.push(make_pair(-a[i],i)); for(long i=1;i<=k;i++) { y=q.top().second; q.pop(); a[y]+=x; q.push(make_pair(-a[y],y)); } for(long i=1;i<=n;i++) sum*=a[i],sum%=mod; sum=-sum; sum=((sum%mod)+mod)%mod; cout<<sum; return 0; } if(a[1]-x*k>0 && rt==2){ a[1]=a[1]-x*k; for(long i=1;i<=n;i++) sum*=a[i],sum%=mod; sum=((sum%mod)+mod)%mod; cout<<sum; return 0; } if(rt==1){ for(long i=1;i<=n;i++) q.push(make_pair(-a[i],i)); for(long i=1;i<=k;i++) { y=q.top().second; q.pop(); a[y]+=x; q.push(make_pair(-a[y],y)); } long long sum=1; for(long i=1;i<=n;i++) sum*=a[i],sum%=mod; sum=-sum; sum=((sum%mod)+mod)%mod; cout<<sum; return 0; } }