单调队列三步:去尾,删头,维护答案。
去尾:从后往前,把不如要加入的点优秀的点删除。
删头:把不在范围的点出队。
这里要维护[i-m,i)范围内的最小前缀和,这样sum[i]-sum[min]可取得每个位置下的最优答案,然后维护最大。
const int N = 2e5 + 5;
int t;
int a[N];
int sum[N];//求每个位置的前缀,比较保留哪个位置
int q[N];
int main()//单调队列解决区间最值
{
int n, m;
cin >> t;
while (t--)
{
int tail = 0, head = 0;
cin >> n >> m;
sum[0] = 0;
f(i, 1, n) { scanf("%d", &a[i]);sum[i] = sum[i - 1] + a[i]; }
f(i, n + 1, n + m-1) { sum[i] = sum[i - 1] + a[i - n]; }
//f(i, 1, 2 * n)cout << sum[i] << " ";
ll mx = -2e9, st = 1, ed = 1;
f(i, 1, n + m-1)//单调增
{
while (head <tail && sum[q[tail-1]-1] > sum[i - 1])//维护前缀递增序列
tail--;//删尾并加入: 删掉比当前大的,因为越后面掌控的范围多。如果前面小的,还应保留
q[tail++] = i;
while (head <tail && i - q[head]+1> m)
head++;//去头,把不在[i-m,i)的老东西弄死
//在[i-m,i-1]之间最小的前缀,保证求差后的最大
if (mx < sum[i] - sum[q[head]-1])
{
mx = sum[i] - sum[q[head]-1];
st = q[head];
ed = i;
}
}
if (ed > n)ed -= n;
cout << mx << " " << st << " " << ed << endl;
}
return 0;
}