1. 程式人生 > >NKOJ 平均值【斜率優化】

NKOJ 平均值【斜率優化】

問題描述

給定一個長度為n的01串,選一個長度至少為L的連續子串,使得子串中數字的平均值最大。 如果有多解,子串長度應儘量小;如果仍有多解,起點編號儘量小。 序列中字元編號1到n,1<=n<=300000, 1<=L<=3000. 例如:對於長度為17的序列00101011011011010,如果L=7,最大平均值為6/8(子序列[7,14]);如果L=5,子序列[7,11]平均值最大,為4/5

輸入格式

第一行,兩個整數n和L 第二行,一個長度為n的01序列

輸出格式

一行,兩個整數,表示所選子序列的起點和終點

ans=max{sumisumj1i(j1)}ans=max\{\frac{sum_i-sum_{j-1}}{i-(j-1)}\}

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define repl(i,x,y) for(ll i=(x);i<(y);i++)
#define repd(i,x,y) for(ll i=(x);i>=(y);i--)
using namespace std;

const ll N=3e5+5;
const ll Inf=1e18;

char ch[N];
ll n,m,l,r,q[N],sum[N];

inline ll read() {
	ll x=0;char ch=getchar();bool f=0;
	while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f?-x:x;
}

ll check(ll x1,ll x2,ll x3,ll x4) {
	return (sum[x2]-sum[x1-1])*(x4-(x3-1))-(sum[x4]-sum[x3-1])*(x2-(x1-1));
}

int main() {
	n=read(),m=read();
	
	scanf("%s",ch+1);
	
	rep(i,1,n) sum[i]=sum[i-1]+ch[i]-'0';
	
	ll ans1=1,ans2=m;
	
	rep(i,m,n) {
		while(r>l+1&&check(q[r-2],i-m,q[r-1],i-m)>=0) r--;
		q[r++]=i-m+1;
		while(r>l+1&&check(q[l],i,q[l+1],i)<=0) l++;
		
		ll slope=check(q[l],i,ans1,ans2);
		
		if(slope>0) ans1=q[l],ans2=i;
	}
	
	printf("%lld %lld\n",ans1,ans2);

	return 0;
}