思路:
一段连续的和可以表示为 sum[ j ] - sum[i - 1]的形式,其中j为终点下标,i为起点下标。所以只要使用单调队列维护区间最小值,预处理下就可以在O(1)的时间复杂度得到sum[i - 1]。
单调队列:
维护滑动区间的最大(小)值。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+10;
ll num[N << 1], minn[N << 1], Q[N << 1], idx[N << 1];//都开两倍,防止数组越界
ll n, k;
void solve(){
for(ll i=n+1; i<=n+k; ++i){//赋值前k个元素
num[i] = num[i - n];
}
for(ll i=1; i<=n+k; ++i){//前缀和
num[i] += num[i - 1];
}
ll h = 1, t = 0;
for(ll i=1; i<=n+k; ++i){//单调队列,维护区间最小值
while(h <= t && num[Q[t]] >= num[i])--t;
Q[++t] = i;
while(i - Q[h] >= k)++h;
minn[i] = num[Q[h]];
idx[i] = Q[h];
}
ll maxn = -1e18;
ll l = 1e18, r = -1;
for(ll i=k+1; i<=n+k; ++i){//找到满足的解
ll sum = num[i] - minn[i-1];
if(sum >= maxn){
maxn = sum;
l = idx[i-1] + 1;
r = i;
}
}
for(ll i=k+1; i<=n+k; ++i){//起点最小化
ll sum = num[i] - minn[i-1];
if(sum == maxn){
ll L = idx[i-1] + 1;
if(L > n)L -= n;
if(L < l){
l = L;
r = i;
}
}
}
if(l > n)l -= n;
if(r > n)r -= n;//处理下标超n情况
printf("%lld %lld %lld\n", maxn, l, r);
}
int main(){
ll t;
scanf("%lld", &t);
while(t--){
scanf("%lld%lld", &n, &k);
for(ll i=1; i<=n; ++i){
scanf("%lld", num+i);
}
solve();
}
return 0;
}
来源:CSDN
作者:baronLJ
链接:https://blog.csdn.net/jun_____/article/details/104651690