WQS二分 - SDOI2016征途


WQS二分适用条件:

  1. 选固定个数的物品,最大化(最小化)权值
  2. 关于个数的函数\(f(x)\)是凸函数

先不考虑限制个数,每次多选一块就要带上一个贡献\(k\)

\(y=kx+b,b=y-kx,b\)要尽量大,就可以找到使这个值最小的位置,我们二分调整\(k\)即可让个数恰好为某个数是,减去我们强行加的贡献便可得到答案

\(ans=m\sum x^2-(\sum x)^2\)

即使\(\sum x^2\)最小

此题是下凸包,每次DP是加上斜率优化,时间复杂度\(O(nlog_n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=3004;
int n,m,s[N],g[N],f[N],q[N];
inline int sqr(int x){return x*x;} 
inline int Y(int x){return f[x]+sqr(s[x]);}
inline void solve(int mid){
	for(int i=1,hd=1,tl=1;i<=n;i++){
		while(hd<tl&&Y(q[hd+1])-Y(q[hd])<2*s[i]*(s[q[hd+1]]-s[q[hd]]))hd++;//!不能<="一样的斜率选前面的g更小" 
		f[i]=f[q[hd]]+mid+sqr(s[i]-s[q[hd]]);
		g[i]=g[q[hd]]+1;
		while(hd<tl&&(Y(i)-Y(q[tl]))*(s[q[tl]]-s[q[tl-1]])<=(Y(q[tl])-Y(q[tl-1]))*(s[i]-s[q[tl]]))tl--;
		q[++tl]=i; 
	}
}
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)s[i]=s[i-1]+read();
	int l=0,r=sqr(s[n]),mid,ans;
	while(l<r){
		mid=l+r>>1;
		solve(mid);
		if(g[n]>m)l=mid+1;
		else{
			r=mid;
			ans=m*(f[n]-mid*m)-sqr(s[n]);
		}
	}
	cout<<ans;
	return (0-0);
}
```![](https://img2020.cnblogs.com/blog/1865199/202003/1865199-20200324173747496-450162326.png)
posted @ 2020-03-24 17:51  starusc  阅读(120)  评论(0)    收藏  举报