CSP-S 2019 划分

\(\text {划分}\)

将一个序列分为\(m\)\((m\)自己决定\()\)并且每一段的和递增 求\(\sum_{i=0}^m s_i ^ 2\)

\(s_i\)为每一段的和

subtask1 \(\text {期望得分 36pts}\)

比较好想 \(f[i][j]\)表示最后一段尾部端点尾\(i\) 头端点为\(j\) 的最小代价
\[f[i][j] = max(f[j][k] + sqr((s[i] - s[j])))\]

subtask2 \(\text {期望得分 64pts}\)

可以优化一下枚举\(i,j\)然后二分找到\(k\) 时间复杂度\(O(n^2 \log n)\)

subtask3 \(\text {期望得分 64pts}\)

可以证明如果一个区间大于前面的区间 且可以分为 \(\text {a b (pre <= a <= b})\)

那么将他分开一定是最优的 那么对于一个\(i\) 我们去维护一个\(f[i]\)并且使得\(s[i] - s[f[i]]\)最小化 答案一定是最优的

我们可以\((n^2)\)找到一个最优的\(f[i]\)然后就得到了\(64pts\)的高分

std \(\text {期望得分 100pts}\)

暴力\(O(n ^ 2)\)未免太暴力了 我们可以想满足条件 \(s[i] - s[j] >= s[j] - s[f[j]]\)

移项可得\(s[i] >= s[j] * 2 - s[f[j]]\) 我们只需找到最后一个满足这个条件的值

然后\(f[i] = j\)就行了 这个东西可以用单调队列去写

int head = 1, tail = 1; 
for (int i = 1; i <= n; i ++ ) { 
    while (head < tail && calc (q[head + 1]) <= s[i]) head ++ ; 
    g[i] = q[head]; 
    while (head < tail && calc (q[tail]) >= calc (i)) tail -- ;     
    q[++tail] = i; 
} 
for (int i = n; i >= 1; i = g[i]) ans = (ans + sqr (s[i] - s[g[i]]));

分析

\(36\)比较\(\text{navie}\)难度普及

\(64\) 分析性质 难度提高

\(100\) 用单调队列维护不难想 关键是前面一步的证明 难度提高+

猜你喜欢

转载自www.cnblogs.com/Hock/p/12213135.html