P4983 忘情(wqs忘情二分)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4983 忘情(wqs忘情二分)相关的知识,希望对你有一定的参考价值。

LINK

为什么叫 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=1nxixˉ)+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=1nxixˉ)+xˉ)2=(1+i=1nxi)2=1+(i=1nxi)2+2i=1nxi

定义 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][k1]+(preiprej)2+2(preiprej)+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 2preiprej+f[i][k]prei22prei1=f[j][k1]+prej22prej

考虑把 2 ∗ p r e i 2*pre_i 2prei看作斜率, 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][k1]+prej22prej)作直线最小化截距

维护一个斜率为正的下凸壳即可.由于 2 ∗ p r e i 2*pre_i 2prei为正且递增,所以有单调性,用单调队列维护

利用斜率优化可以在 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忘情二分)的主要内容,如果未能解决你的问题,请参考以下文章

wqs二分的边界

CF739EGosha is hunting(WQS二分套WQS二分)

python之基础篇——模块与包

[算法相关]wqs 二分

CF739E Gosha is hunting(wqs二分套wqs二分)

P2619 [国家集训队]Tree I(wqs二分)