题意:给定长度为N的01串,选一个长度不小于L的区间,使得子串中,数字的平均值最大。如果有多解,区间尽量小,如果任然有多解,起点下边尽量小。
首先求出前缀和a[i],为前n项数字和。区间i-j的平均值即为(a[j]-a[i-1])/(j-i+1);算法思路,从小到大枚举t,找出t'<=l,并且满足斜率t't最大。
中间重点在求t'时,需要维护一个凹区间,凹区间具有斜率递增的性质,因此可以再插入一个新的节点时,将区间内新形成的凸加点删除。然后在凹区间内找到与t斜率最大的一个点,更新结果。
#include<bits/stdc++.h>
using namespace std;
double a[100010];
double p[100010];
char s[100010];
int cmp(int x1,int x2,int x3,int x4)
{
return (a[x1]-a[x2-1])*(x3-x4+1)-(a[x3]-a[x4-1])*(x1-x2+1);
}
int main()
{
int t;
scanf("%d",&t);
int n,L;
while(t--)
{
scanf("%d%d%s",&n,&L,s);
int len=n;
for(int i=1;i<=len;i++)
a[i]=a[i-1]+s[i-1]-'0';
int l=1,r=L;
int i=0,j=0;
for(int t=L;t<=len;t ++) {//从小到大枚举t,找到t'<=t-L并斜率最大
while(j-i>1&&cmp(t-L,p[j-2],t-L,p[j-1])>=0) j--; //删除上凸的点,被删除的在右边连续
p[j++]=t-L+1; //增加新元素
while(j-i>1&&cmp(t,p[i+1],t,p[i])>=0) i++; //确定新的切点,因为切点的x坐标一定是不断增加的
int c=cmp(r,l,t,p[i]);
if(c<0||c==0&&(r-l)>(t-p[i])) {
l=p[i];r=t ;
}
}
printf("%d %d\n",l,r);
}
return 0;
}