题目大意:
给出一个有N个数字(N<=10^5)的环状序列,让你求一个和最大的连续子序列。这个连续子序列的长度小于等于K。
思路:
我们用单调队列去维护一个开始的下标位置,随后我们去枚举结束的位置。
维护方法:对于每个j,我们插入s[j-1](为什么不是s[j]? 队列里面维护的是区间开始的下标,j是区间结束的下标),插入时从队尾插入。为了保证队列的单调性,我们从队尾开始删除元素,直到队尾元素比当前需要插入的元素优(本题中是值比待插入元素小,位置比待插入元素靠前,不过后面这一个条件可以不考虑),就将当前元素插入到队尾。之所以可以将之前的队列尾部元素全部删除,是因为它们已经不可能成为最优的元素了,因为当前要插入的元素位置比它们靠前,值比它们小。我们要找的,是满足(i>=j-k+1)的i中最小的s[i],位置越大越可能成为后面的j的最优s[i]。
#include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #include <sstream> #include <iostream> #define LL long long #define INF 0x3f3f3f3f #define ls nod<<1 #define rs (nod<<1)+1 const double eps = 1e-10; const int maxn = 2e5 + 10;; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; int a[maxn],sum[maxn]; int main() { int T; scanf("%d",&T); while (T--) { int n,m; scanf("%d%d",&n,&m); sum[0] = 0; for (int i = 1;i <= n;i++) { scanf("%d",&a[i]); a[i+n] = a[i]; } for (int i = 1;i <= 2*n;i++) sum[i] = sum[i-1] + a[i]; deque<int> q; int ans = -INF; int l = 0,r = 0; for (int i = 1;i <= n+m-1;i++) { while (!q.empty() && sum[i-1] < sum[q.back()]) q.pop_back(); while (!q.empty() && q.front() < i-m) q.pop_front(); q.push_back(i-1); if (sum[i]-sum[q.front()] > ans) { ans = sum[i]-sum[q.front()]; l = q.front()+1; r = i; } } if (r > n) r %= n; printf("%d %d %d ",ans,l,r); } return 0; }