数形结合-下凸折线求最大平均值

传送门
这块的内容参考了国家集训队2004周源的论文。

数形结合

将题目中要求的求最大平均值,转为 i i i和其对应的前缀和作为图中的坐标点 ( i , s u m [ i ] ) (i,sum[i]) (i,sum[i])
a v g ( i , j ) = k ( i , j ) = s u m [ i ] − s u m [ j ] i − j avg(i,j)=k(i,j)= \frac{sum[i]-sum[j]}{i-j} avg(i,j)=k(i,j)=ijsum[i]sum[j]
现在我们希望找到斜率最大的直线

下凸折线

下凸折线
只有满足如上形状的折线叫下凸折线,即各个折线段从左到右,斜率越来越大。下图的折线段不能被称为下凸折线。
在这里插入图片描述
维护这样一个点集组成的折线集合-下凸折线。
点集是一个双端队列,在队尾放入新点。
答案更新时,如果对于点集中的 q [ l + 1 ] q[l+1] q[l+1],当前点 k k k与之形成的斜率大于当前点 k k k q [ l ] q[l] q[l]形成的斜率。则弹出队首的 q [ l ] q[l] q[l].答案用队首进行更新即可。
严格地证明比较难就略过。。。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 110522
#define ll long long
int a[MAXN];
int sum[MAXN];
int q[MAXN];
int check(int i,int j,int k)
{
    
    
    return double(sum[k]-sum[j])/(k-j)>=double(sum[j]-sum[i])/(j-i);
}
double k(int i,int j)
{
    
    
    return double(sum[j]-sum[i])/(j-i);
}
int main()
{
    
    
    int n,f;
    scanf("%d%d",&n,&f);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+a[i];
    int ans=0;
    int r=0;
    int l=1;
    for(int i=f;i<=n;i++)
    {
    
    
        while(r>l&&check(q[r-1],q[r],i)==0){
    
    r--;}
        q[++r]=i-f;
        while(r>l&&k(q[l],i)<=k(q[l+1],i)){
    
    l++;}
        ans=max(ans,int(1000*k(q[l],i)));
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_43353639/article/details/107387338