题目大意:给出n(n<=100000)个数组成的环状序列,求最大的不超过k个数连续和,输出最大和及其开始点,结束点。当有多个最大值时,输出开始点最小的。仍有多解,输出长度最小的。
题解:
1.
首先将序列延长至2n,对2*n>=i>n令a[i]=a[i-n],破环为链。
2.
设s[i]=a[1]+a[2]+……+a[i],设f[i]表示以i为结束节点的最大连续和,则有f[i]=max(s[i]-s[j])=s[i]-min(s[j]),其中i-m<=j<=i-1,该方程满足使用单调队列的条件。
3.
为了满足输出方案的要求,维护一个单调不降的队列。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; const int maxn=300021; int s[maxn],ans,head,tail,q[maxn],sec,n,m,a[maxn],st,en; int value(int x) { if(x>n)x=x-n; return x; } int main() { scanf("%d",&sec); for(int z=1;z<=sec;z++) { scanf("%d%d",&n,&m); s[0]=0; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; for(int i=n+1;i<=2*n;i++) s[i]=s[i-1]+a[i-n];//破环为链 head=0;tail=0;q[0]=0;st=1;en=1;ans=-(1<<30); for(int i=1;i<=2*n;i++) { while(head<=tail && q[head]<i-m)head++;//弹出超过范围的队列首元素 if(s[i]-s[q[head]]>ans)//更新最大值 { ans=s[i]-s[q[head]]; st=value(q[head]+1);en=value(i); } else if(s[i]-s[q[head]]==ans && value(q[head]+1)<st) { st=value(q[head]+1);en=value(i); } while(head<=tail && s[i]<s[q[tail]])tail--;//将s[i]加入队列 tail++;q[tail]=i; } printf("%d %d %d\n",ans,st,en); } return 0; }