HU 3415 Max Sum of Max-K-sub-sequence(DP+単調なキュー)

1554 ワード

テーマリンク:
http://acm.hdu.edu.cn/showproblem.php?pid=3415
件名:
長さnのリングを指定します.その中の一つの長さがkに等しい区間より小さいことを求めて、それを最大にして、出力は最大と区間の左右の端点です.
分析:
直接暴力dp[i]=sum[i]-sum[i-k+1+j]を思い付きます. (0<=j==k)
しかし、明らかに暴力がタイムアウトします.単独でキューを使ってメンテナンスしたいです.sum配列です.
sumをできるだけ小さくして前にします.処理するときは環を展開します.sumはプレフィックスと預けられます.
コードは以下の通りです
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

const int maxn = 100010;

int a[maxn*2],sum[maxn*2];

int main()
{
    int t,n,k;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&k);
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            sum[i]=sum[i-1]+a[i];
            a[i+n]=a[i];
        }
        for(int i=n+1;i<=2*n;i++)
            sum[i]=sum[i-1]+a[i];
        deque<int >Q;
        int ans = -1000000000,st=0,ed=0;
        for(int i=1;i<=n+k-1;i++){
            while(!Q.empty()&&sum[i-1]<sum[Q.back()])//        
                Q.pop_back();
            while(!Q.empty()&&Q.front()<i-k)//  k             
                Q.pop_front();
            Q.push_back(i-1);
            if(sum[i]-sum[Q.front()]>ans){
                ans=sum[i]-sum[Q.front()];
                st=Q.front()+1;
                ed=i;
            }
        }
        if(ed>n) ed-=n;
        printf("%d %d %d
",ans,st,ed); } return 0; }