Loading [MathJax]/extensions/tex2jax.js

BZOJ4709:「JSOI2011」柠檬 – 题解

主要思路

首先发现两端并不会影响,所以可以直接做分段的线性 DP。正常思考暴力,发现每次都需要枚举颜色,非常麻烦。但是我们可以发现一个特别好的性质:每一次区间的颜色都取决于这个区间的最后一个元素,因为意识到选其他颜色时,我们大可把这个区间的右端点进行调整,使得下一个区间不会更劣。

了解到这个之后,DP 就是 \(\Theta(n^2)\) 的了,考虑进一步优化。对这个 DP 的式子进行展开:

\[ f_{j – 1} + a_i(pre_j – 1)^2 = (2a_i pre_i)(pre_j – 1) + f_i – a_i pre_i^2 \]

当然,这是已经做成直线式的 DP 式子了,设置变量 \(x = (pre_j – 1), k = (2a_i pre_i), b = f_i – a_i pre_i^2, y = f_{j – 1} + a_i(pre_j – 1)^2\)。从原式中发现平方项,且增长速度快于 \(y = x\),故具有决策单调性,所以只需要单调栈即可。加入元素之前,我们需要找到上两个决策之间的决策边界,然后判断一下决策边界是否大于新的决策边界,如果是就弹掉(因为不会前者更优)。

代码

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// BZ4709.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200;
typedef long long ll;
int n, ai[MAX_N], idx[MAX_N], cnt[MAX_N];
ll dp[MAX_N];
deque<int> stk[MAX_N];
ll calc(int x, ll y) { return dp[x - 1] + 1LL * ai[x] * y * y; }
int check(int x, int y)
{
int l = 1, r = n, res = n + 1;
while (l <= r)
{
int mid = (l + r) >> 1;
if (calc(x, mid - idx[x] + 1) >= calc(y, mid - idx[y] + 1))
r = mid - 1, res = mid;
else
l = mid + 1;
}
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 1, x; i <= n; i++)
{
scanf("%d", &ai[i]), x = ai[i];
idx[i] = ++cnt[ai[i]];
while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= check(stk[x].back(), i))
stk[x].pop_back();
stk[x].push_back(i);
while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= idx[i])
stk[x].pop_back();
dp[i] = calc(stk[x].back(), idx[i] - idx[stk[x].back()] + 1);
}
printf("%lld\n", dp[n]);
return 0;
}
// BZ4709.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e5 + 200; typedef long long ll; int n, ai[MAX_N], idx[MAX_N], cnt[MAX_N]; ll dp[MAX_N]; deque<int> stk[MAX_N]; ll calc(int x, ll y) { return dp[x - 1] + 1LL * ai[x] * y * y; } int check(int x, int y) { int l = 1, r = n, res = n + 1; while (l <= r) { int mid = (l + r) >> 1; if (calc(x, mid - idx[x] + 1) >= calc(y, mid - idx[y] + 1)) r = mid - 1, res = mid; else l = mid + 1; } return res; } int main() { scanf("%d", &n); for (int i = 1, x; i <= n; i++) { scanf("%d", &ai[i]), x = ai[i]; idx[i] = ++cnt[ai[i]]; while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= check(stk[x].back(), i)) stk[x].pop_back(); stk[x].push_back(i); while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= idx[i]) stk[x].pop_back(); dp[i] = calc(stk[x].back(), idx[i] - idx[stk[x].back()] + 1); } printf("%lld\n", dp[n]); return 0; }
// BZ4709.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200;

typedef long long ll;

int n, ai[MAX_N], idx[MAX_N], cnt[MAX_N];
ll dp[MAX_N];
deque<int> stk[MAX_N];

ll calc(int x, ll y) { return dp[x - 1] + 1LL * ai[x] * y * y; }

int check(int x, int y)
{
    int l = 1, r = n, res = n + 1;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (calc(x, mid - idx[x] + 1) >= calc(y, mid - idx[y] + 1))
            r = mid - 1, res = mid;
        else
            l = mid + 1;
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1, x; i <= n; i++)
    {
        scanf("%d", &ai[i]), x = ai[i];
        idx[i] = ++cnt[ai[i]];
        while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= check(stk[x].back(), i))
            stk[x].pop_back();
        stk[x].push_back(i);
        while (stk[x].size() >= 2 && check(stk[x][stk[x].size() - 2], stk[x].back()) <= idx[i])
            stk[x].pop_back();
        dp[i] = calc(stk[x].back(), idx[i] - idx[stk[x].back()] + 1);
    }
    printf("%lld\n", dp[n]);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *