题目给出的是一个环状的序列,所以可以在序列后面复制前k个数字。如果用sum[i]来表示复制过后的
序列的前i个数的和,那么任意一个子序列 [i..j]的和就等于s[j]-s[i-1]。对于每一个j,用s[j]减去
最小的一个s[i](i>=j-k+1)就可以得到以j为终点长度不大于k的和最大的序列了。将原问题转化为这样
一个问题后,就可以用单调队列解决了。单调队列确实是一种很实用的结构,本题中保持单调递增,队
首元素就是我们需要的那个最小元素,保证待插入的j-1位置对应的sum值要大于队尾元素。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <queue> #include <algorithm> using namespace std; const int MAXN = 100010; const int INF = 0x3fffffff; int a[MAXN], sum[MAXN << 1]; int N, K, n, st, end, ans; void Read() { scanf("%d%d", &N, &K); sum[0] = 0; for(int i = 1; i <= N; i ++) { scanf("%d", &a[i]); sum[i] = sum[i - 1] + a[i]; } for(int i = N + 1; i < N + K; i ++) { sum[i] = sum[i - 1] + a[i - N]; } n = N + K - 1; } void cal() { deque<int> q; q.clear(); ans = -INF; for(int j = 1; j <= n; j ++) { while(!q.empty() && sum[j - 1] < sum[q.back()]) //队尾元素大于 { q.pop_back(); } while(!q.empty() && q.front() < j - K) { q.pop_front(); } q.push_back(j - 1); //插入j - 1 if(sum[j] - sum[q.front()] > ans) { ans = sum[j] - sum[q.front()]; st = q.front() + 1; end = j; } } } int main() { int T; scanf("%d", &T); while(T --) { Read(); cal(); if(end > N) end %= N; printf("%d %d %d\n", ans, st, end); } return 0; }
浙公网安备 33010602011771号