题意:求连续的子序列使得平均数最大。
分析:按照《浅谈数形结合思想在信息学竞赛中的应用》中的做法,维护一个下凸的决策区间,但是注意原作者提到单调栈做法并不是指决策是随着右端点单调的,而是指最优解是随着左端点单调的。
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#define INF 0x3f3f3f3f
#define eps 1e-9
using namespace std;
int T,l,n,x,y,ans,Stack[100005],f[100005];
char s[100005];
long long jud(int x1,int y1,int x2,int y2,int x3,int y3)
{
return 1ll*(x1-x3)*(y2-y3)-1ll*(y1-y3)*(x2-x3);
}
double dis(int a,int b)
{
return (f[b]-f[a])*1.0/(b-a);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&l);
scanf("%s",s+1);
for(int i = 1;i <= n;i++) f[i] = s[i] - '0' + f[i-1];
int front = 1,tail = 1,ans1 = 0,ans2 = l;
double ans = 0;
for(int i = l;i <= n;i++)
{
while(tail - front >= 2 && dis(Stack[tail-1],i-l) <= dis(Stack[tail-1],Stack[tail-2])) tail--;
Stack[tail++] = i-l;
int left = front,right = tail-1;
while(left != right)
{
int mid = (left+right)/2 + 1;
if(jud(Stack[mid-1],f[Stack[mid-1]],Stack[mid],f[Stack[mid]],i,f[i]) >= 0) left = mid;
else right = mid - 1;
}
if(dis(Stack[right],i) > ans)
{
ans = (1.0*f[i]-f[Stack[right]])/(i-1.0*Stack[right]);
ans1 = Stack[right];
ans2 = i;
}
else
if(dis(Stack[right],i) == ans && ans2 - ans1 > i - Stack[right])
{
ans1 = Stack[right];
ans2 = i;
}
}
cout<<ans1+1<<" "<<ans2<<endl;
}
}