传送门 \looparrowright

题目描述

今天 qwb\text{qwb} 要参加一个数学考试,这套试卷一共有 nn 道题,每道题 qwb\text{qwb} 能获得的分数为 aia_iqwb\text{qwb} 并不打算把这些题全做完,他想选总共 2k2k 道题来做,并且期望他能获得的分数尽可能的大,他准备选 22 个不连续的长度为 kk 的区间,即 [L,L+1,L+2,....,L+k1][L,L+1,L+2,....,L+k-1][R,R+1,R+2,...,R+k1][R,R+1,R+2,...,R+k-1](RL+k)(R \geqslant L+k)

输入描述

第一行一个整数 TT(T10)(T\leqslant 10),代表有 TT 组数据。
接下来一行两个整数 n,kn,k(1n200000)(1\leqslant n\leqslant 200000)(1k,2kn)(1\leqslant k,2k \leqslant n)
接下来一行 nn 个整数 a1,a2,...,ana_1,a_2,...,a_n(100,000ai100000)(-100,000\leqslant a_i\leqslant 100000)

输出描述

输出一个整数,qwb\text{qwb} 能获得的最大分数。

示例1

输入
2
6 3
1 1 1 1 1 1
8 2
-1 0 2 -1 -1 2 3 -1
输出
6
7

分析

定义 sum[i]=j=ii+k1a[j]sum[i]=\sum\limits_{j=i}^{i+k-1} a[j] ,每次枚举 LLsum[L]sum[L],然后查询区间 [L+k,n][L+k,n] 内长度为 kk 的区间最大值,即查询 maxL+kink+1sum[i]\max\limits_{L+k\leqslant i\leqslant n-k+1} sum[i],这显然是一个简单的 RMQ\text{RMQ} 问题,可用 ST\text{ST} 表维护 sum[i]sum[i] 的区间最大值。sum[L]+maxL+kink+1sum[i]sum[L]+\max\limits_{L+k\leqslant i\leqslant n-k+1} sum[i] 有可能成为答案,O(n)O(n) 枚举 LL,即可得到答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<cstdio>
#include<algorithm>
#include<iostream>
#define N 200003
#define ll long long
using namespace std;
int a[N];
ll sum[N];
int _log2[N];
ll f[N][18];//ST表
int n,k;
int nn;
/*预处理以2为底的对数值(向下取整)*/
void pre()
{
_log2[1]=0;
int i;
for(i=2;i<N;i++) _log2[i]=_log2[i>>1]+1;
}
/*-------ST表初始化------*/
void init()
{
int i,j;
for(i=1;i<=nn;i++) f[i][0]=sum[i];//边界值
for(j=1;(1<<j)<=nn;j++)
{
for(i=1;i+(1<<j)-1<=nn;i++)
{
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
}
}
int main()
{
int _;
pre();
for(cin>>_;_;_--)
{
scanf("%d%d",&n,&k);
int i;
for(i=1;i<=n;i++) scanf("%d",a+i);
nn=n-k+1;
ll temp=0;
/*===========================*/
for(i=1;i<=n;i++)//计算sum[i]
{
if(i<=k) temp+=a[i];
else
{
sum[i-k]=temp;
temp=temp-a[i-k]+a[i];
}
}
sum[nn]=temp;
/*===========================*/
init();//处理ST表
ll ans=-1000000000000000000;
for(i=1;i<=n-2*k+1;i++)//枚举第一个区间
{
int l=i+k,r=nn;
int s=_log2[r-l+1];
//查询区间[i+k,nn]内最大的sum[i]
ans=max(ans,sum[i]+max(f[l][s],f[r-(1<<s)+1][s]));
}
printf("%lld\n",ans);
}
return 0;
}