BZOJ3971 [WF2013]Матрёшка

*XXXIV. BZOJ3971 [WF2013]Матрёшка

摘自 DP 做题记录 II 例题 XXXIV.

仍然是神仙区间 DP。

直接设状态 \(f_{i,j}\) 表示区间 \([i,j]\) 的答案不太方便,考虑最终答案一定由若干段 \(1\sim m\) 的套娃合并在一起,所以我们设 \(f_{i,j}\) 表示\([i,j]\) 合并成一个套娃的最小代价并设 \(g_i\) 表示把长度 \(i\) 的前缀合并成若干个合法套娃的最小代价,则答案为 \(g_n\)

考虑怎么算 \(f_{l,r}\)首先 \(l,r\) 不能有相同大小的套娃,记为 \(\mathrm{diff}(l,r)\)。然后枚举断点 \(k\),注意到 \([l,k]\) 所有大于 \(\min [k+1,r]\) 的套娃需要被拆开,以及 \([k+1,r]\) 所有大于 \(\min[l,k]\) 的套娃需要被拆开,这个可以二维前缀和预处理 \(s_{l,V}\) 表示 \(1\sim l\) 有多少个大小在 \(1\sim V\) 的套娃并 \(\mathcal{O}(1)\) 计算。记 \(\mathrm{merge}(l,k,r)\) 表示合并 \([l,k]\)\([k+1,r]\) 的代价,则

\[f_{l,r}=\begin{cases}\min_{k=l}^{r-1}f_{l,k}+f_{k+1,r}+\mathrm{merge}(l,k,r)& \mathrm{diff}(l,r)\\\infty&\mathrm{otherwise}\end{cases} \]

\(g\) 的转移是 trivial 的:

\[g_i=\min_{j=0}^{i-1}\begin{cases}g_j+f_{j+1,i} & \mathrm{mex}(j+1,i)=i-j+1\\\infty&\mathrm{otherwise}\end{cases} \]

\(g_n=\infty\) 则输出 impossible,时间复杂度 \(\mathcal{O}(n^3)\)

 const int N = 500 + 5;
const int inf = 1e8;
 
int n, a[N], f[N], g[N][N], s[N][N], mi[N][N], mex[N][N], ck[N][N];
int cal(int l, int r, int x1, int x2) {
    return s[r][x2] - s[l - 1][x2] - s[r][x1 - 1] + s[l - 1][x1 - 1];
}
void cmex(int l, int r) {
    static int buc[N]; mem(buc, 0, N), mex[l][r] = 1;
    for(int i = l; i <= r; i++) buc[a[i]] = 1;
    while(buc[mex[l][r]]) mex[l][r]++;
}
void csam(int l, int r) {
    static int buc[N]; mem(buc, 0, N);
    for(int i = l; i <= r; i++)
        ck[l][r] |= buc[a[i]], buc[a[i]] = 1;
}
int mer(int l, int k, int r) {
    assert(l <= k && k < r && !ck[l][r]);
    int m1 = mi[l][k], m2 = mi[k + 1][r];
    return cal(l, k, m2, N - 1) + cal(k + 1, r, m1, N - 1);
}
int main() {
    cin >> n, mem(mi, 31, N), mem(g, 31, N), mem(f, 31, N), f[0] = 0;
    for(int i = 1; i <= n; i++) s[i][a[i] = read()]++, g[i][i] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j < N; j++)
            s[i][j] += s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1];
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++)
            cmex(i, j), csam(i, j), mi[i][j] = min(mi[i][j - 1], a[j]);
    for(int len = 2; len <= n; len++)
        for(int l = 1, r = len; r <= n; l++, r++)
            if(!ck[l][r]) for(int k = l; k < r; k++)
                g[l][r] = min(g[l][r], g[l][k] + g[k + 1][r] + mer(l, k, r));
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < i; j++)
            if(mex[j + 1][i] == i - j + 1)
                f[i] = min(f[i], f[j] + g[j + 1][i]);
    if(f[n] >= inf) puts("Impossible");
    else cout << f[n] << endl;
    return 0;
}
posted @ 2021-09-26 14:35  qAlex_Weiq  阅读(288)  评论(0编辑  收藏  举报