【题意】:给出一个长度为n的环状序列,求一个长度不超过k的和最大的连续子序列。
【题解】:很容易想到O(n*k)的做法,但是赤裸裸的TLE。
先求出所有前n项和。
枚举每一个位置 i 作为结束点,现在我们要找到最小的sum[j], i - k <= j < i .
朴素做法是直接枚举O(k),改进一点的做法是用线段树查询区间最大值O(logk).
但是可以证明有单调性,故可以用单调队列优化,查询直接优化到O(1)。
所以最终复杂度是O(n)。
【代码】:
1 #include "iostream"
2 #include "cstdio"
3 #include "cstring"
4 #include "algorithm"
5 #include "vector"
6 #include "queue"
7 #include "cmath"
8 #include "string"
9 #include "cctype"
10 #include "map"
11 #include "iomanip"
12 using namespace std;
13 #define pb push_back
14 #define mp make_pair
15 #define fi first
16 #define se second
17 #define lc(x) (x << 1)
18 #define rc(x) (x << 1 | 1)
19 #define lowbit(x) (x & (-x))
20 #define ll long long
21 #define maxn 100050
22 const int inf = 1 << 30;
23 int que[maxn<<1], head, tail;
24 int sum[maxn<<1];
25 int val[maxn];
26 int n, k;
27 int ans, s, t;
28 int main() {
29 int T;
30 scanf("%d", &T);
31 while(T--) {
32 scanf("%d%d", &n, &k);
33 sum[0] = 0;
34 for(int i = 1; i <= n; i++) {
35 scanf("%d", &val[i]);
36 sum[i] = val[i] + sum[i-1];
37 }
38 for(int i = n + 1; i <= 2 * n; i++) {
39 sum[i] = sum[i-1] + val[i-n];
40 }
41 ans = -inf;
42 head = tail = 0;
43 que[tail++] = 0;
44 for(int i = 1; i <= 2 * n; i++) {
45 while(head < tail && que[head] < i - k) head++;
46 if(ans < sum[i] - sum[que[head]]) {
47 ans = sum[i] - sum[que[head]];
48 s = que[head] + 1, t = i;
49 }
50 while(head < tail && sum[que[tail-1]] > sum[i]) tail--;
51 que[tail++] = i;
52 }
53 if(s > n) s -= n;
54 if(t > n) t -= n;
55 cout << ans << " " << s << " " << t << endl;
56 }
57 return 0;
58 }
59