[i,j]这个子串的值是点i-1,与点j之间的斜率。
求所有子串的最大值就是求i-1到j的斜率的最大值。
每个点的横坐标为在子串中的位置(第几个),纵坐标为sum[i-1]。
问题转化为求n+1个点中(加入坐标原点)找两个点,横坐标至少相差L且斜率最大。
这个思路想清楚后剩下的就全是数学问题了,详见紫书P243。
代码写的不好。修修补补,cmp不是在比较斜率,而是在比较串的值,单调栈中cmp的调用就是凑合用着来算斜率的。
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
int n,L;
char str[maxn];
int sum[maxn];
vector<int>vec;
int cmp(int a,int b,int c,int d)
{
if(a==0&&b==0) return 1;
int x1=b-a+1;
int x2=d-c+1;
int y1=sum[b]-sum[a-1];
int y2=sum[d]-sum[c-1];
int ans=x1*y2-x2*y1;
return ans;
}
void cr(int x)
{
while(vec.size()>=2)
{
int a=vec[vec.size()-2];
int b=vec[vec.size()-1];
if(cmp(a,b-1,b,x-1)<=0) vec.pop_back();
else break;
}
vec.push_back(x);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&L);
scanf("%s",str+1);
vec.clear();
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+str[i]-'0';
int l,r;
l=r=0;
for(int i=L;i<=n;i++)
{
cr(i-L+1);
unsigned int k=0;
while(1)
{
if(k==vec.size()-1) break;
if(cmp(vec[k],i,vec[k+1],i)<0) break;
k++;
}
int ans=cmp(l,r,vec[k],i);
if(ans<0) continue;
else if(ans==0)
{
int l1=r-l;
int l2=i-vec[k];
if(l1<l2) continue;
else if(l1==l2)
{
if(l<vec[k]) continue;
}
}
l=vec[k];
r=i;
}
printf("%d %d\n",l,r);
}
return 0;
}