題目點此跳轉
思路
題目意思是給你一全環形的數組(頭尾相接), 求所有長度不大於k的區間中 元素和 最大 的的區間 及 最大的元素和。
區間的和可以使用前綴和相減求出,設sum[i]爲從0到i的前綴和, 區間[i, j]的和即爲sum[j] - sum[i-1]; 那麼對於每一個區間尾j,我們只要求出它前面i個內最小的sum[i]即可,顯然單調隊列是可以解決的。
每次區間尾j右移時,將j所在元素入隊(注意刪除隊列中比它大的), 然後將不在此區間內的隊首元素出隊,剩下的隊首元素即爲此區間內的最小值。
注意此題的數組是環形的,多解時輸出的順序也有要求,所以區間尾j的初始值要處理好。
代碼
#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("ot.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e6 + 100;
const LL INF = 0x7fffffff;
const int dir[5][2] = {0,0,-1,0,1,0,0,-1,0,1};
const int MOD = 1e9 + 7;
const double eps = 1e-6;
int n, k, a[maxn], sum[maxn], ans, L, R;
int q[maxn], p[maxn], ft, rr;
int main() {
#ifdef _LOCAL
IN; //OT;
#endif // _LOCAL
int t; cin >> t;
while(t--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 1; i < k; ++i) a[n+i] = a[i];
sum[0] = a[n];
for(int i = 1; i < n+k; ++i) sum[i] = sum[i-1] + a[i];
ft = rr = 0; ans = -INF;
q[rr++] = sum[0]; p[rr-1] = 0;
for(int i = 1; i <= k-1; ++i) {
while(rr > ft && q[rr-1] >= sum[i]) --rr;
q[rr++] = sum[i], p[rr-1] = i;
}
if(ans < sum[k]-q[ft]) { ans = sum[k]-q[ft]; L = p[ft]%n+1; R = (k-1)%n+1; }
for(int i = k+1; i < n+k; ++i) {
while(rr > ft && q[rr-1] >= sum[i-1]) --rr;
q[rr++] = sum[i-1], p[rr-1] = i-1;
if(p[ft] < i-k) ++ft;
if(ans < sum[i] - q[ft]) { ans = sum[i]-q[ft]; L = p[ft]%n+1; R = (i-1)%n+1; }
else if(ans == sum[i] - q[ft]) {
if(L > p[ft]%n + 1) L = p[ft]%n+1, R = (i-1)%n+1;
else if( L == p[ft]%n + 1 && R-L > (i-1)%n-p[ft]%n) L = p[ft]%n+1, R = (i-1)%n+1;
}
}
printf("%d %d %d\n", ans, L, R);
}
return 0;
}