Date:2022.01.03
题意:假设下标从1开始,只能翻转一次某一子序列,求下标为奇数的最大和。
思路①:首先显然交换的区间长度为奇数是没有意义的,因此如果交换区间长度只能固定为偶数。模拟一下不难发现翻转本质是让一个区间的值从奇数项和变为偶数项和,因此只需要找到偶数项和 - 奇数项和值最大的那个区间即可,我们先暴力试一下。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
typedef unsigned long long LL;
LL t,n,m,k;
LL a[N],b[N];
LL sumji[N],sumou[N];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
LL sums=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(i%2)
{
sums+=a[i];
sumji[i]=sumji[i-1]+a[i];
sumou[i]=sumou[i-1];
}
else
{
sumji[i]=sumji[i-1];
sumou[i]=sumou[i-1]+a[i];
}
}
LL res=sums;
for(int i=2;i<=n;i+=2)//选定长度
{
for(int l=1;l<=n-i+1;l++)//起始点
{
LL r=l+i-1;
LL sumj=sumji[r]-sumji[l-1];
LL sumo=sumou[r]-sumou[l-1];
LL summ=sums-sumj+sumo;
res=max(res,summ);
}
}
cout<<res<<endl;
}
return 0;
}
然而会t掉。
思路②:大体思路是没有错的,不过在统计哪个区间的奇数和 - 偶数和最大时,沿用了“最大子段和”的思想。因为每个区间值的转换原理都是奇数和->偶数和,因此我们用差分数组表示每个奇数项 - 偶数项,取区间和即为 + 若干偶数项 and - 若干奇数项,用一个当前和记录每一步的和。当前和<0时,表示当前区间已不能选,当前和置为0并继续向后取【说不太清楚,自己模拟一下吧…】。除此之外,奇数项 - 偶数项有两种,一种奇数项 - 前面的偶数项,另一种是奇数项 - 后面的偶数项,因此处理两次。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
typedef long long LL;
LL t,n,m,k;
LL a[N],b[N];
LL l[N],r[N];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
LL sum=0,res=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(i%2) sum+=a[i];
}
LL now=0;
for(int i=2;i<=n;i+=2)
{
now+=a[i]-a[i-1];
res=max(res,now);
if(now<0) now=0;
}
now=0;
for(int i=3;i<=n;i+=2)
{
now+=a[i-1]-a[i];
res=max(res,now);
if(now<0) now=0;
}
cout<<sum+res<<endl;
}
return 0;
}