P4072 [SDOI2016]征途(wqs二分+斜率优化)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4072 [SDOI2016]征途(wqs二分+斜率优化)相关的知识,希望对你有一定的参考价值。
把 n n n个数划分为 m m m段,求最小的方差.
设序列和 ∑ i = 1 n x i = S \\sum\\limits_{i=1}^nx_i=S i=1∑nxi=S,最后选取的每段的数字和为 L i L_i Li
D = 1 m ∗ ∑ i = 1 m ( E − L i ) 2 = 1 m ∑ i = 1 m ( E 2 + L i 2 − 2 E ∗ L i ) D=\\frac{1}{m}*\\sum\\limits_{i=1}^m(E-L_i)^2=\\frac{1}{m}\\sum\\limits_{i=1}^m (E^2+L_i^2-2E*L_i) D=m1∗i=1∑m(E−Li)2=m1i=1∑m(E2+Li2−2E∗Li)
其中 E = S m E=\\frac{S}{m} E=mS
m ∗ D = m E 2 + ∑ i = 1 m L i 2 − 2 E ∗ ∑ i = 1 m L i m*D=mE^2+\\sum\\limits_{i=1}^mL_i^2-2E*\\sum\\limits_{i=1}^mL_i m∗D=mE2+i=1∑mLi2−2E∗i=1∑mLi
m ∗ D = m E 2 + ∑ i = 1 m L i 2 − 2 m E 2 m*D=mE^2+\\sum\\limits_{i=1}^mL_i^2-2mE^2 m∗D=mE2+i=1∑mLi2−2mE2
得到 m 2 D = m ∗ ∑ i = 1 m L i 2 − S 2 m^2D=m*\\sum\\limits_{i=1}^mL_i^2-S^2 m2D=m∗i=1∑mLi2−S2
如何最小化 ∑ i = 1 m L i 2 \\sum\\limits_{i=1}^m L_i^2 i=1∑mLi2??
定义 g ( i ) g(i) g(i)表示当 m = i m=i m=i时上式的最小值
大胆猜测是个斜率为负的下凸函数(分成一段肯定巨大,分成两段肯定变化显著,越往后分,减小的越慢)
这个下凸函数的斜率逐渐增大
考虑用一条斜率为 k k k的直线去切这个凸包,如何求出切点??
y = k x + b y=kx+b y=kx+b,因为是切线,所以让 b b b最小的点 ( x , g ( x ) ) (x,g(x)) (x,g(x))就是切点
那么 b = g ( x ) − k x b=g(x)-kx b=g(x)−kx
我们的 x x x每增加 1 1 1, b b b值就会减小 k k k,也就是每段的权值会减去 k k k
现在的问题是可以分成任意段,求最小值
考虑定义 f [ i ] f[i] f[i]表示 [ 1 , i ] [1,i] [1,i]的分段代价
f [ i ] = min { f [ j ] + ( p r e i − p r e j ) 2 − k } f[i]=\\min\\{f[j]+(pre_i-pre_j)^2-k\\} f[i]=min{f[j]+(prei−prej)2−k}
2 ∗ p r e i ∗ p r e j + f [ i ] + k − p r e i 2 = f [ j ] + p r e j 2 2*pre_i*pre_j+f[i]+k-pre_i^2=f[j]+pre_j^2 2∗prei∗prej+f[i]+k−prei2=f[j]+prej2
斜率优化可以做到 O ( n ) O(n) O(n)
加上带权二分,总体复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
#include <bits/stdc++.h>
using namespace std;
const int maxn =3e3+10;
int n,m,f[maxn],pre[maxn],a[maxn],line[maxn];
int head = 1, tail = 0, q[maxn];
int x(int i){ return pre[i]; }
int y(int i){ return f[i]+pre[i]*pre[i]; }
double slove(int i,int j)
{
return ( 1.0*y(i)-y(j) )/( 1.0*x(i)-x(j) );
}
int isok(int k)
{
head = 1, tail = 0, q[++tail] = 0;
for(int i=1;i<=n;i++)
{
while( head+1<=tail && slove(q[head],q[head+1])<2*pre[i] ) head++;
int las = q[head];
f[i] = f[las] +( pre[i]-pre[las] )[SDOI2016]征途