WQS二分 - SDOI2016征途


WQS二分适用条件:
- 选固定个数的物品,最大化(最小化)权值
- 关于个数的函数\(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);
}
```
作者:starusc
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号