P4983 忘情(wqs忘情二分)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4983 忘情(wqs忘情二分)相关的知识,希望对你有一定的参考价值。
为什么叫
w
q
s
wqs
wqs二分呢?忘情二分显然更好听
题意
我们定义一段序列的值为
( ( ∑ i = 1 n x i ∗ x ˉ ) + x ˉ ) 2 x ˉ 2 \\frac{((\\sum\\limits_{i=1}^nx_i*\\bar x)+\\bar x)^2}{\\bar x^2} xˉ2((i=1∑nxi∗xˉ)+xˉ)2
给定一段长度为 n n n的数组,分成 m m m段,最小化每段的和值。
( ( ∑ i = 1 n x i ∗ x ˉ ) + x ˉ ) 2 x ˉ 2 = ( 1 + ∑ i = 1 n x i ) 2 = 1 + ( ∑ i = 1 n x i ) 2 + 2 ∗ ∑ i = 1 n x i \\frac{((\\sum\\limits_{i=1}^nx_i*\\bar x)+\\bar x)^2}{\\bar x^2}\\\\ =(1+\\sum\\limits_{i=1}^nx_i)^2\\\\ =1+(\\sum\\limits_{i=1}^nx_i)^2+2*\\sum\\limits_{i=1}^nx_i xˉ2((i=1∑nxi∗xˉ)+xˉ)2=(1+i=1∑nxi)2=1+(i=1∑nxi)2+2∗i=1∑nxi
定义 f [ i ] [ k ] f[i][k] f[i][k]表示前 i i i个数分成 k k k段的最小和值
f [ i ] [ k ] = f [ j ] [ k − 1 ] + ( p r e i − p r e j ) 2 + 2 ∗ ( p r e i − p r e j ) + 1 f[i][k]=f[j][k-1]+(pre_i-pre_j)^2+2*(pre_i-pre_j)+1 f[i][k]=f[j][k−1]+(prei−prej)2+2∗(prei−prej)+1
直接 d p dp dp是 O ( n 2 k ) O(n^2k) O(n2k)的,我们稍微化简一下
2 ∗ p r e i ∗ p r e j + f [ i ] [ k ] − p r e i 2 − 2 ∗ p r e i − 1 = f [ j ] [ k − 1 ] + p r e j 2 − 2 ∗ p r e j 2*pre_i*pre_j+f[i][k]-pre_i^2-2*pre_i-1=f[j][k-1]+pre_j^2-2*pre_j 2∗prei∗prej+f[i][k]−prei2−2∗prei−1=f[j][k−1]+prej2−2∗prej
考虑把 2 ∗ p r e i 2*pre_i 2∗prei看作斜率, p r e j pre_j prej看作自变量,等式右边看作因变量
那么现在斜率固定,所以我们需要找到一个点 ( p r e j , f [ j ] [ k − 1 ] + p r e j 2 − 2 ∗ p r e j ) (pre_j,f[j][k-1]+pre_j^2-2*pre_j) (prej,f[j][k−1]+prej2−2∗prej)作直线最小化截距
维护一个斜率为正的下凸壳即可.由于 2 ∗ p r e i 2*pre_i 2∗prei为正且递增,所以有单调性,用单调队列维护
利用斜率优化可以在 O ( n k ) O(nk) O(nk)的时间内得到解
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+10;
int f[1009][1009],n,m,a[maxn],pre[maxn];
int head = 1, tail = 0, q[maxn];
int x(int i){ return pre[i]; }
int y(int i,int k){ return f[i][k]+pre[i]*pre[i]-2*pre[i]; }
double slove(int i,int j,int k)
{
return ( 1.0*y(i,k)-y(j,k) )/( 1.0*x(i)-x(j) );
}
signed main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i], pre[i] = ( pre[i-1]+a[i] );
for(int i=1;i<=n;i++) f[i][1] = pre[i]*pre[i]+2*pre[i]+1;
for(int k=2;k<=m;k++)
{
head = 1, tail = 0, q[++tail] = k-1;
for(int i=k;i<=n;i++)//前i个数分成k段
{
while( head+1<=tail && slove( q[head],q[head+1],k-1 )<=2*pre[i] ) head++;
int las = q[head]以上是关于P4983 忘情(wqs忘情二分)的主要内容,如果未能解决你的问题,请参考以下文章
CF739EGosha is hunting(WQS二分套WQS二分)