大概就是求一个环形序列里长度不超过k的最大子段和。
因为是环形,所以我们可以把序列复制一遍。
对于每个i,我们要求的就是一个j,使得i-j+1<=k且j~i这段和是最大的,然后和答案比较。
那怎么求呢?把前缀和处理出来,i~j的序列和就是s[i]-s[j-1],i是每次枚举的,已经确定,所以我们就是需要求最小的s[j-1]。
我们可以用递增的单调队列来维护前缀和,队列中存的是下标。
另外就是一些细节处理。
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N=100005*2;
int n,T,k,data[N],q[N],l,r,s[N],ans,beg,en;
int get(){
char x=getchar();int p=0,t=1;
while (x<'0' || x>'9') {
if (x=='-') t=-1;
x=getchar();
}
while (x>='0' && x<='9') p=p*10+x-'0',x=getchar();
return p*t;
}
int main(){
T=get();
while (T--){
n=get();k=get();
for (int i=1;i<=n;i++) {
data[i]=get();
s[i]=s[i-1]+data[i];
}
for (int i=1;i<=k;i++)
s[i+n]=data[i]+s[i+n-1];
beg=en=1;ans=data[1];
l=r=1;q[1]=0;
for (int i=1;i<=n+k;i++){
for (;l<=r && i-q[l]>k;l++); //剔除不合法的值
if (l<=r){
if (s[i]-s[q[l]]>ans) ans=s[i]-s[q[l]],beg=q[l]+1,en=i;
if (s[i]-s[q[l]]==ans){
if (q[l]+1<beg) beg=q[l]+1,en=i;
if (q[l]+1==beg) en=min(en,i);
}
}
for (;s[q[r]]>=s[i] && r>=l;r--);
q[++r]=i;
}
if (beg>n) beg-=n;
if (en>n) en-=n;
printf("%d %d %d\n",ans,beg,en);
}
return 0;
}