Max Sum of Max-K-sub-sequence

题目大意:
给出一个有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\n",ans,l,r);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/-Ackerman/p/12398579.html