HDU 3415 Max Sum of Max-K-sub-sequence 单调队列

孤街浪徒 提交于 2020-03-04 17:26:04

思路:

一段连续的和可以表示为 sum[ j ] - sum[i - 1]的形式,其中j为终点下标,i为起点下标。所以只要使用单调队列维护区间最小值,预处理下就可以在O(1)的时间复杂度得到sum[i - 1]。

单调队列:

维护滑动区间的最大(小)值。

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll N = 1e5+10;

ll num[N << 1], minn[N << 1], Q[N << 1], idx[N << 1];//都开两倍,防止数组越界
ll n, k;

void solve(){
    for(ll i=n+1; i<=n+k; ++i){//赋值前k个元素
        num[i] = num[i - n];
    }
    for(ll i=1; i<=n+k; ++i){//前缀和
        num[i] += num[i - 1];
    }
    ll h = 1, t = 0;
    for(ll i=1; i<=n+k; ++i){//单调队列,维护区间最小值
        while(h <= t && num[Q[t]] >= num[i])--t;
        Q[++t] = i;
        while(i - Q[h] >= k)++h;
        minn[i] = num[Q[h]];
        idx[i] = Q[h];
    }
    ll maxn = -1e18;
    ll l = 1e18, r = -1;
    for(ll i=k+1; i<=n+k; ++i){//找到满足的解
        ll sum = num[i] - minn[i-1];
        if(sum >= maxn){
            maxn = sum;
            l = idx[i-1] + 1;
            r = i;
        }
    }
    for(ll i=k+1; i<=n+k; ++i){//起点最小化
        ll sum = num[i] - minn[i-1];
        if(sum == maxn){
            ll L = idx[i-1] + 1;
            if(L > n)L -= n;
            if(L < l){
                l = L;
                r = i;
            }
        }
    }
    if(l > n)l -= n;
    if(r > n)r -= n;//处理下标超n情况
    printf("%lld %lld %lld\n", maxn, l, r);
}

int main(){
    ll t;
    scanf("%lld", &t);
    while(t--){
        scanf("%lld%lld", &n, &k);
        for(ll i=1; i<=n; ++i){
            scanf("%lld", num+i);
        }
        solve();
    }
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!