题意:
\(n\) 个数,第 \(i\) 个数的原始权值是 \(w_i\),按某种顺序选择这些数。
若当前是第 \(i\) 次选数,选择的当前数的权值为 \(k\) ,则其他所有未被选过的数的权值均加上 \((-1)^{i+k+1} \times k\)。
求最大的权值和。
我们知道,\((-1)^x\) 的结果只有两种,\(1\) 或 \(-1\) ,既然我们要求最大权值和,那我们就尽量加上一个正数,我们发现,\((-1)^{i+k+1} \times k\) 的正负与 \(k\) 和 \(i+k+1\) 有关,于是我们可以对这两个进行分类讨论。
当 \(k\geq 0\) 的时候,我们希望 \((-1)^x\) 是正的,也就是希望 \(x\) 是偶数。
当 \(k<0\) 的时候,我们希望 \((-1)^x\) 是负的,也就是希望 \(x\) 是奇数。
于是我们可以讨论 \(i+k+1\) 的奇偶,由于 \(k+1\) 是定值,我们不妨预处理出来,讨论 \(i\) 的奇偶。
拿样例来说:
k | 1 | -1 | -2 | 2 | -3 | 3 | 4 |
---|---|---|---|---|---|---|---|
k+1 | 2 | 0 | -1 | 3 | -2 | 4 | 5 |
对于第一个数 \(1\) ,\(k+1=2\) ,因为 \(k>0\),\(2\) 是偶数,所以我们希望此时的 \(i\) 是偶数。
对于第三个数 \(-1\),\(k+1=-1\) 因为 \(k<0\) ,\(-1\) 是奇数,所以我们希望 \(i\) 是偶数。
对于第四个数 \(2\),\(k+1=3\) 因为 \(k>0\) ,\(3\) 是奇数,所以我们希望 \(i\) 是奇数。
对于第五个数 \(-3\),\(k+1=-2\) 因为 \(k<0\) ,\(-2\) 是偶数,所以我们希望 \(i\) 是奇数。
于是我们总结出一个规律,
\(\geq 0\) | \(<0\) | |
---|---|---|
奇数 | 奇数 | 偶数 |
偶数 | 偶数 | 奇数 |
行代表 \(k\) 的正负 ,列代表 \(k+1\) 奇偶,填的数代表我们希望当前的 \(i\) 的奇偶。
然后我们将给出的数分成两类,一类是希望 \(i\) 是奇数的,放入 \(js\) 数组,一类是希望 \(i\) 是偶数的,放入 \(os\) 数组。
for(int i=1;i<=n;i++)
{
scanf("%lld",&mp[i])
int x=mp[i]+1;//k+1
if(abs(x)%2==0) //偶数
{
if(mp[i]>=0)//大于 0
os[++o]=mp[i];//希望他加上一个偶数
else
js[++j]=mp[i];
}
else //奇数
{
if(mp[i]<=0)
os[++o]=mp[i];//希望他加上一个偶数变成奇数使得答案为正
else
js[++j]=mp[i];
}
}
我们考虑选择一个数对答案的贡献,给剩下的数加 \((-1)^{i+k+1} \times k\),就相当于我们在最后加一个 \(k\times res\) ,\(res\) 表示除了当前数序列里还未选择的数的个数。\(res\) 是确定的,我们可以贪心的选择让当前的 \(k\) 是大的,当然,我们考虑的是加上一个正数的时候。后面如果要减去一个数,我们要尽量减去一个最小的值。数的原始值也是要加的,直接预处理就可以。
因此,我们可以将两个数组排下序,对答案没有影响。
下面我们考虑如何选数,如果两个数组里面的数是相等时,我们直接一个里面选一个选完就可以了,但是如果两个数组里面的个数不相等,我们就要继续讨论。
sort(os+1,os+1+o,cmp);
sort(js+1,js+1+j,cmp);
while(min(o,j))
{
Ans+=abs(js[j])*cnt,j--,cnt--;
Ans+=abs(os[o])*cnt,o--,cnt--;
}
我们知道两个数组在上述操作后一定有一个为空,考虑选择剩下的数组里的数。
如果 \(os\) 数组有剩余:
我们的数组的定义是希望在偶数次被选择,也就是在偶数次选择是对答案贡献更大,所以我们尽量实现让它在偶数次被选择,如果不行,就让它在奇数次选择是损失尽可能的小。
我们当前的操作肯定既有奇数也有偶数,当前则选择为奇数是,我们选择序列中最小的数,做到最小损失,当前选择为偶数时,我们选择序列中最大的数,做到最大贡献。
\(js\) 数组也是这样的思路。
至于实现,我是用了两个指针一个在前一个在后扫,因为序列是已经排过序的,可以直接得到最大最小值。
最后,因为我们是尽量加一个正数,但我们两个数组里存的是原始值,因此做加减的时候要取绝对值。其实数组里存绝对值也可以,只是加减的时候要注意。
下面附上完整代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define int long long
#define INF 0x3f3f3f3f
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e5+5;
int n;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
int mp[M],js[M],os[M],j,o,Ans,cnt;
bool cmp(int a,int b)
{
return abs(a)<abs(b);
}
signed main()
{
// freopen("date.in","r",stdin);
n=read();cnt=n-1;
for(int i=1;i<=n;i++)
{
mp[i]=read();
int x=mp[i]+1;
if(abs(x)%2==0) //偶数
{
if(mp[i]>=0)//大于 0
os[++o]=mp[i];//希望他加上一个偶数
else
js[++j]=mp[i];
}
else //奇数
{
if(mp[i]<=0)
os[++o]=mp[i];//希望他加上一个偶数变成奇数使得答案为正
else
js[++j]=mp[i];
}
Ans+=mp[i];//预处理答案,提前先加上
}
sort(os+1,os+1+o,cmp);
sort(js+1,js+1+j,cmp);
while(min(o,j))//能够依次减的先都减去
{
Ans+=abs(js[j])*cnt,j--,cnt--;
Ans+=abs(os[o])*cnt,o--,cnt--;
}
if(o==0&&j)//对于剩下的数
{
int l=1,r=j;
while(l<=r)
{
if((n-cnt)%2)//当前选的的是第奇数个数
Ans+=abs(js[r])*cnt,cnt--,r--;
else
Ans-=abs(js[l])*cnt,cnt--,l++;
}
}
if(j==0&&o)//偶数有剩下
{
int l=1,r=o;
while(l<=r)
{
if((n-cnt)%2==0)//当前选的的是第偶数个数
Ans+=abs(os[r])*cnt,cnt--,r--;
else
Ans-=abs(os[l])*cnt,cnt--,l++;
}
}
printf("%lld\n",Ans);
return 0;
}
/*
5
1 2 3 4 5
8
-2 5 -3 6 7 8 11 13
*/
昨天比赛的时候死活调不出来这道题,今天拿同学的代码拍了拍发现就了个小地方。大家一定要注意复制一段相似的代码的时候变量名一定要改!!!