题面描述
t
i
a
s
c
h
tiasch
tiasch 18岁生日的时候,
l
q
p
1
8
31
lqp18_{31}
lqp1831给她看了一个神奇的序列
A
1
A_1
A1,
A
2
A_2
A2, …,
A
N
A_N
AN. 她被允许选择不超过
M
M
M 个连续的部分作为自己的生日礼物。ftiasch想要知道选择元素之和的最大值。你能帮助她吗?
输入格式
第一行两个整数
N
N
N,
M
M
M。
第二行N个整数 A 1 A_1 A1~ A N A_N AN。
输出格式
一个整数,表示答案。
样例输入
5 2
2 -3 2 -1 2
样例输出
5
数据范围与约定
对于100%的数据:
N
,
M
<
=
1
0
5
N,M<=10^5
N,M<=105,
∣
A
i
∣
<
=
1
0
4
|A_{i}|<=10^4
∣Ai∣<=104
思考
首先要求元素之和最大,则我们可以尝试先把所有正数
A
i
A_i
Ai统计起来,那么区间怎么算呢,开个数组
S
S
S,
S
S
S用来统计相同数区间的和
a
n
s
ans
ans首先等于所有
S
i
S_i
Si为正数的和,用
c
n
t
cnt
cnt统计选的
S
S
S的个数,
若
c
n
t
≤
m
cnt≤m
cnt≤m,直接输出即可。
若
c
n
t
>
m
cnt>m
cnt>m,则考虑每次取绝对值最小的区间出来,与相邻区间合并后,由于对结果影响较小,则对未来状态更优。
另外,首尾为负直接丢掉,因为没法与相邻正区间合并,若合并会导致得不到最优解。
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define ll long long
using namespace std;
const int N=1e5+10;
struct node
{
int c,p;
bool operator <(const node a)const{return abs(c)>abs(a.c);}
};
priority_queue<node>q;
int s[N],pre[N],nxt[N];bool v[N];
void del(int x)
{
v[x]=0;
pre[nxt[x]]=pre[x];
nxt[pre[x]]=nxt[x];
}
int main()
{
int n,m;scanf("%d%d",&n,&m);int tot=1;memset(v,true,sizeof(v));
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
if((ll)s[tot]*x>=0)s[tot]+=x;//一段连续的同号数和
else s[++tot]=x;
}
int cnt=0,ans=0;
for(int i=1;i<=tot;i++)
{
node k;
k.c=s[i],k.p=i;q.push(k);
if(s[i]>0)ans+=s[i],cnt++;
nxt[i]=i+1,pre[i]=i-1;
}
while(cnt>m)
{
cnt--;
node k=q.top();int p=k.p;
while(!v[p])
{
q.pop();
k=q.top();p=k.p;
}
q.pop();
if(pre[p]&&nxt[p]!=tot+1)ans-=abs(s[p]);//选 ,合并两个正数和一个最小负数,尽量减少损失;
//选,不选最小正数或合并两个负数和一个最小正数,使未来状态更优
else if(s[p]>0)ans-=s[p];//选,没得抵消,去除一个在头(尾)最小正数
else {cnt++;continue;}//抵消cnt--的影响
s[p]+=s[nxt[p]]+s[pre[p]];
del(nxt[p]),del(pre[p]);
k.c=s[p];
q.push(k);
}
printf("%d\n",ans);
return 0;
}
``