luogu P4767 [IOI2000]邮局 wqs二分

https://blog.csdn.net/a_forever_dream/article/details/105581221
https://blog.csdn.net/a_forever_dream/article/details/105587292

#include <cstdio>
#include <cstring>
#define maxn 3010
#define mid (l+r>>1)

int n,m,a[maxn],sum[maxn],ans;
int l=0,r=1000000;
//贡献 
int w(int l,int r)
{
//	int mid = l+r >>1; 
	//在l 到 r 之间放的贡献 
	//l      r  
	//  mid 
	//
	return sum[r]-sum[mid]-(r-mid)*a[mid]+(mid-l)*a[mid]-(sum[mid-1]-sum[l-1]);
}
struct node
{
	int pos,l,r;
};//pos统治区间[l,r]
node q[maxn];
int f[maxn],s[maxn],t;
int calc(int from,int now,int cost)
{
	return from>now?999999999:f[from]+w(from+1,now)+cost;
}
int solve(int cost)
{
	q[t=1]= {0,1,n};
	for(int i=1; i<=n; i++)
	{
		int l=1,r=t,pos;
		while(l<=r)
			q[mid].l<=i?(l=(pos=mid)+1):(r=mid-1);
		//找到统治i的点,找到i在哪个区间内
		f[i]=calc(q[pos].pos,i,cost);
		s[i]=s[q[pos].pos]+1;

		pos=-1;//从最后一个区间看起,看看i能统治多少个区间
		while(t>0&&calc(i,q[t].l,cost)<=calc(q[t].pos,q[t].l,cost))pos=q[t--].l;
		//最后那个不能全部统治的区间二分一下,能统治多少是多少
		if(t>0&&calc(i,q[t].r,cost)<=calc(q[t].pos,q[t].r,cost))
		{
			l=q[t].l,r=q[t].r;
			while(l<=r)calc(i,mid,cost)<=calc(q[t].pos,mid,cost)?(r=(pos=mid)-1):(l=mid+1);
			q[t].r=pos-1;
		}
		if(pos!=-1)q[++t]= {i,pos,n}; //加上i统治的新区间
	}
	return s[n];
}

int main()
{
	//n个数字,分成m段 
	scanf("%d %d",&n,&m);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
	//二分斜率 
	while(l<=r)
	{
		//以斜率为mid的直线去切,当与y轴截距最大的时候,就是切线
		//此时得到的横坐标,如果大于等于m,就可以更新,
		if(solve(mid)>=m)
			ans=f[n]-m*mid,l=mid+1;
		//如果小于等于m了,那么就说明斜率太大了,该变小,然后让横坐标右移 
		else
			r=mid-1;
	}
	printf("%d",ans);
}

原文地址:https://www.cnblogs.com/QingyuYYYYY/p/13895661.html