[CSP2019]划分

题目

点这里看题目。

分析

出题人已经开始拿高精作为考点了吗

0pts ~ 24pts

数据太小,小到你甚至很难想到专门对付这些部分分的算法

36pts

这应该是一个经典的问题, USACO 曾经考过类似的题目。

思想很简单,既然我们要求分出来的段单调递增,我们就把每一段的两个端点都放到状态里面。于是就有:

(f(i,j)):前 (j) 个数,最后一段的左端点为 (i) 的最小时间。

顺手设 (s_i=sum_{j=1}^i a_j)

转移显然:

[f(i,j)=min_{egin{aligned}&0le k<i,\s_{i-1}-&s_{k-1}le s_j-s_{i-1}end{aligned}}{f(k,i-1)}+(s_j-s_{i-1})^2 ]

时间复杂度 (O(n^3))

64pts

对上面的 DP 进行优化。

考虑到 (i) 固定的时候,(j) 增大, (s_j-s_{i-1}) 也会增大,那么 (k) 最小值就会变小,并且 (j) 越大, (k) 越小

因而我们可以确定 (i) ,单调枚举 (j) ,同时维护 (k) 的最小值和 (min{f(k,i-1)}) ,就可以做到均摊 (O(1)) 转移,总时间就降到了 (O(n^2))

88pts ~ 100pts

首先我们需要一个很显然的不等式:

[forall a, bge0, (a+b)^2ge a^2+b^2 ]

这就意味着,我们应该尽量让每一段的和变小

那么,我们也可以知道,决策应该是单调的。更具体地说,我们设:

(f(i)):前 (i) 个数进行划分的最小结果。

对应有 (d(i)) :前 (i) 个数进行划分,最后一段的划分点,即满足:

[f(i)=f(d(i))+(s_i-s_{d(i)})^2 ]

就应该有:(forall 1<ile n, d(i-1)le d(i))

考虑一个点如何能成为一个决策点。对于 (i, j(i<j)),如果 (i) 可以作为 (j) 的决策点,就意味着:

[egin{aligned}&s_i-s_{d(i)}le s_j - s_i\Rightarrow & 2s_i-s_{d(i)}le s_jend{aligned} ]

因而我们可以设 (g(i)=2s_i-s_{d(i)})

下面我们又可以得到一个重要的性质:

[forall i,exists j,k,i<j<k,g(j)<g(i)<s_kRightarrow d(k) ot= i ]

感性理解就是,比你小,还比你强,你就退役了。

简单来说,由于 (g(i)>g(j)) ,则所有能用 (i) 进行决策的点,一定也可以用 (j) 决策。同时,根据贪心性质,从 (i) 划分得到的段比从 (j) 要大,因此不可能选择 $ i $​ 作为决策点。

因此我们可以维护维护一个单调栈,保证内部的 (g) 是不降的。查询就可以在里面进行二分,这样是 (O(nlog_2n)) 的。

更进一步的,由于 (s) 也是不降的,因而我们可以使用单调队列维护决策点。这样就是 (O(n)) 了。

关于高精度,答案显然不会超过 (1.6 imes 10^{33}) 。因而,如果你懒了,你可以用 __int128 ,不然也可以用两个 long long 拼在一块,再不济就写个高精得了。

代码

64pts

typedef long long LL;

const LL INF = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5005;

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
} 

LL f[MAXN][MAXN];
LL s[MAXN];

	for( int i = 1 ; i <= N ; i ++ ) f[1][i] = s[i] * s[i];
	for( int i = 2 ; i <= N ; i ++ )
		for( int j = 1 ; j <= N ; j ++ )
			f[i][j] = INF;
	LL mn; int l;
	for( int i = 2 ; i <= N ; i ++ )
	{
		mn = INF, l = i;
		for( int j = i ; j <= N ; j ++ )
		{
			while( l && s[i - 1] - s[l - 1] <= s[j] - s[i - 1] )
			{
				mn = MIN( mn, f[l][i - 1] );
				l --;
			}
			f[i][j] = mn + ( s[j] - s[i - 1] ) * ( s[j] - s[i - 1] );
		}
	}
	LL res = INF;
	for( int i = 1 ; i <= N ; i ++ ) res = MIN( res, f[i][N] );

100pts

#define G( x ) ( 2ll * S[x] - S[dec[x]] )

typedef long long LL;
typedef __int128 LLL;

const LLL E = 1;
const int MAXN = 4e7 + 5, MAXM = 1e5 + 5;

int dec[MAXN], q[MAXN];
LL S[MAXN];

	int h = 1, t = 0;
	q[++ t] = 0;
	for( int i = 1 ; i <= N ; i ++ )
	{
		while( h < t && G( q[h + 1] ) <= S[i] ) h ++;
		dec[i] = q[h];
		while( h < t && G( q[t] ) >= G( i ) ) t --;
		q[++ t] = i;
	}
	LLL ans = 0;
	for( int i = N ; i ; i = dec[i] )
		ans = ans + E * ( S[i] - S[dec[i]] ) * ( S[i] - S[dec[i]] );
原文地址:https://www.cnblogs.com/crashed/p/13373778.html