CSP-S划分 解题报告

n <= 10

爆搜即可

n <= 50

什么乱搞

n <= 400

有一个 \(n^3\) 的 dp

设 dp[i][j] 表示最后一段为 j+1~i 时的最小值

直接三层循环转移即可

dp[1][0] = 0;
for(int i = 1;i <= n;i ++)
{
    dp[i][0] = qi[i] * qi[i];
    for(int j = 1;j < i;j ++)
        for(int k = 0;k < j;k ++)
        {
            if(qi[i] - qi[j] >= qi[j] - qi[k]) dp[i][j] = min(dp[i][j], dp[j][k] + (qi[i] - qi[j]) * (qi[i] - qi[j]));
        }
}

这里 qi 数组为前缀和

n <= 5000

通过大胆的打表我们发现,这个玩意在 j 最大,k 最大且合法时取到最小值

然后我们对于每个 j ,在转移时额外把 k 记录到 l 数组中,可以把 k 的循环优化掉,变成 \(n^2\)

dp[0] = 0;
for(int i = 1;i <= n;i ++)
{
    for(int j = i - 1;j >= 0;j --)
    {
        if(qi[i] - qi[j] >= qi[j] - qi[l[j]])
        {
            dp[i] = dp[j] + (qi[i] - qi[j]) * (qi[i] - qi[j]);
            l[i] = j;
            break;
        }
    }
}

n <= 500000

把要求的式子变变形,发现:

\(qi[i] >= qi[j] * 2 - qi[l[j]]\)

由于qi[i]一直在增大,所以当新加入的 j 的 \(qi[j] * 2 - qi[l[j]]\) 比之前的要小的话,后面的数只会从 j 转移,不会从之前的数转移。

所以可能被转移的,是一个 \(qi[j] * 2 - qi[l[j]]\) 的序列。

这不一眼单调队列

然后搞成了 \(O(n)\)

dp[0] = 0;
q.push_back(0);
for(int i = 1;i <= n;i ++)
{
    int back = -1;
    while(!q.empty() && qi[i] >= 2 * qi[q.front()] - qi[l[q.front()]]) back = q.front(), q.pop_front();
    if(back != -1) q.push_front(back);
    int j = q.front();
    dp[i] = dp[j] + (qi[i] - qi[j]) * (qi[i] - qi[j]);
    l[i] = j;
    while(!q.empty() && 2 * qi[i] - qi[l[i]] <= 2 * qi[q.back()] - qi[l[q.back()]]) q.pop_back();
    q.push_back(i);
}

算法复杂度已经正确了,但这道题出题人硬是要搞一个高精。如果用__int128转移那么1G空间都不够你用的。

所以就这样吧

posted @ 2022-10-26 09:10  _maze  阅读(35)  评论(0)    收藏  举报