Herself32's Blog

BZOJ1010「HNOI2008」题解

Link

Description

\(n\) 件物品,第 \(i\) 件的长度为 \(c_i\).

要把这个物品序列划分成 \(k\) 个仅端点相交的子串,如果把区间 \([l,r]\) 划分为一个子串,那么这个子串的长度为 \(x = r - l + \sum_{i=l}^r c_i\).

这个子串的代价为 \((x - L)^2\), 其中 \(L\) 是给定常数,求一种划分方案使得所有子串代价和最小。 \(n \leq 5 \times 10^4\)

Solution

斜率优化板子。

考虑朴素的 \(\mathbf{DP}\), 设 \(\mathbf{DP}_i\) 表示考虑了前 \(i\) 件物品时的答案,那么枚举上一个决策点,可以推出转移:

\[\mathbf{DP}_i = \min_{1 \leq j < i} \left \{ \mathbf{DP}_{j} + \left( \sum_{k=j+1}^i c_k + i - j - 1 + L\right)^2 \right \} \]

用前缀和代替一下式子里面的 \(\Sigma\), 再设 \(f_i = sum_i + i\) 简化一下.

\[\mathbf{DP}_i = \min_{1 \leq j < i} \left \{ \mathbf{DP}_{j} + \left(f_i - f_j - 1 - L\right)^2 \right \} \]

显然暴力 \(\mathbf{DP}\)\(\Theta(n^2)\) 的,发现这个式子很符合斜率优化的运用条件,那么直接推式子。

考虑设 \(j_1, j_2\) 是位置 \(i\) 的两个决策点,且满足 \(j_2\) 优于 \(j_1\), 那么显然有

\[\mathbf{DP}_{j_1} + \left( f_i - f_{j_1} - 1 - L\right)^2 \geq \mathbf{DP}_{j_2} + \left( f_i - f_{j_2} - 1 - L\right)^2 \]

把平方拆开,然后稍微合并一下同类项得:

\[2f_i(f_{j_2} + 1 + L) - 2f_i(f_{j_1} + 1 + L) \geq \mathbf{DP}_{j_2} - \mathbf{DP}_{j_1} + \left( f_{j_2} + 1 + L\right)^2 - \left( f_{j_1} + 1 + L\right)^2 \]

\(g_i = (f_i + L + 1) ^ 2\), 那么原式即

\[2f_i \geq \frac {\mathbf{DP}_{j_2} - \mathbf{DP}_{j_1} + g_{j_2} - g_{j_1}}{f_{j_2} - f_{j_1}} \]

也就是说,如果满足上面那个不等式,我们可以得出决策 \(j_2\) 优于 \(j_1\).

斜率优化是说把这个转移方程式写成 \(y = kx+b\) 的形式, 那么自然可以令 \(\mathbf {DP}_i + g_i = y, f_i = x\), 那么上面不等式的右面就是一个 \(\frac {\Delta y}{\Delta x}\) 的一次函数斜率形式,当两个决策点之间的连线斜率 \(k \leq 2f_i\) 时满足决策点 \(j_2\) 优于决策点 \(j_1\).

显然所有决策点之间构成了一个下凸壳, 那么直接单调队列算一下即可。

Code

/* Headers */
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#define FOR(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define ROF(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define FORL(i,a,b,c) for(long long i=(a);i<=(b);i+=(c))
#define ROFL(i,a,b,c) for(long long i=(a);i>=(b);i-=(c))
#define FORR(i,a,b,c) for(register int i=(a);i<=(b);i+=(c))
#define ROFR(i,a,b,c) for(register int i=(a);i>=(b);i-=(c))
#define RevEdge(x) x^1
#define lowbit(x) x&(-x)
#define LeftChild(x) x<<1
#define RightChild(x) (x<<1)+1
#define CLOSE_IN() fclose(stdin);
#define CLOSE_OUT() fclose(stdout);
#define FILE_IN(x) freopen(x,"r",stdin);
#define FILE_OUT(x) freopen(x,"w",stdout);
#define IOS(x) std::ios::sync_with_stdio(x)
#define Dividing() printf("-----------------------------------\n");
namespace FastIO {
#define gc() (iS == iT ? (iT = (iS = ibuff) + fread(ibuff, 1, SIZ, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
    const int SIZ = 1 << 21 | 1;
    char* iS, * iT, ibuff[SIZ], obuff[SIZ], * oS = obuff, * oT = oS + SIZ - 1, fu[110], cc;
    int fr;
    inline void out() {
        fwrite(obuff, 1, oS - obuff, stdout);
        oS = obuff;
    }
    template<class Type>
    inline void read(Type& x) {
        x = 0;
        Type y = 1;
        for (cc = gc(); (cc > '9' || cc < '0') && cc != '-'; cc = gc());
        cc == '-' ? y = -1 : x = (cc & 15);
        for (cc = gc(); cc >= '0' && cc <= '9'; cc = gc())
            x = x * 10 + (cc & 15);
        x *= y;
    }
    template<class Type>
    inline void print(Type x, char text = '\n') {
        if (x < 0)
            * oS++ = '-', x *= -1;
        if (x == 0)
            * oS++ = '0';
        while (x)
            fu[++fr] = x % 10 + '0', x /= 10;
        while (fr);
            * oS++ = fu[fr--];
        * oS++ = text;
        out();
    }
    inline void prints(char x[], char text = '\n') {
        for (register int i = 0; x[i]; ++i)
            * oS++ = x[i];
        * oS++ = text;
        out();
    }
}
using namespace FastIO;
using std::pow;
template<typename T>
inline T max(T a, T b) {return (a > b) ? a : b;}
template<typename T>
inline T min(T a, T b) {return (a > b) ? b : a;}
/* definitions */
#define int long long
const int MAXN = 5e4 + 10;
int n, L, c[MAXN], sum[MAXN], f[MAXN], g[MAXN], dp[MAXN];
int q[MAXN], head, tail;
/* functions */
inline double slope(int x, int y) {
	return (double) (dp[y] + g[y] - dp[x] - g[x]) / (f[y] - f[x]);
}
#undef int
int main(int argc, char *argv[]) {
	//FILE_IN("data.in");
	read(n); read(L);
	FOR(i, 1, n, 1) {
		read(c[i]); sum[i] = sum[i - 1] + c[i];
		f[i] = sum[i] + i; g[i] = (f[i] + L + 1) * (f[i] + L + 1);
	} g[0] = (L + 1) * (L + 1);
	FOR(i, 1, n, 1) {
		while(head < tail && slope(q[head], q[head + 1]) <= (f[i] << 1)) head++;
		dp[i] = dp[q[head]] + (f[i] - f[q[head]] - L - 1) * (f[i] - f[q[head]] - L - 1);
		while(head < tail && slope(q[tail], i) < slope(q[tail - 1], q[tail])) tail--; q[++tail] = i;
	} printf("%lld\n", dp[n]);
	return 0;
}

posted on 2020-10-21 16:39  Herself32  阅读(99)  评论(0编辑  收藏  举报

导航