[CF1025D] Recovering BST

[CF1025D] Recovering BST - 区间dp

Description

给定一棵二叉搜索(n le 700) 的所有点权,构造二叉树满足任意两个直接相邻的点的权值的 GCD 不是 1

Solution

这种关于二叉搜索树中序遍历的 DP 问题,设状态时忽略根而考虑区间,因为这个区间作为一个子树,它的父亲一定是区间左端点的左边的点或者区间右端点的右边的点

(f[i][j]) 表示以 ([i,j-1]) 作为 j 的左孩子是否合法

(g[i][j]) 表示以 ([i+1,j]) 作为 i 的右孩子是否合法

转移时,考虑一个区间 ([l,r]),枚举它的根 (k),需要满足 (f[i][k]) 并且 (g[k][j])

如果此时 (k)(l-1) 相连是合法的,那么就可以转移到 ([l-1,r])

如果此时 (k)(r+1) 相连是合法的,那么就可以转移到 ([l,r+1])

总之,设状态时设的是“半棵子树”的合法性,转移时首先考虑一个区间可能有哪些根,然后分别考虑这些点为根时,向外的转移情况

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 705;

// 设 $f[i][j]$ 表示以 $[i,j-1]$ 作为 j 的左孩子是否合法
// 设 $g[i][j]$ 表示以 $[i+1,j]$ 作为 i 的右孩子是否合法

int f[N][N], g[N][N], a[N], n, x[N][N];

signed main()
{
    ios::sync_with_stdio(false);

    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= n; i++)
        f[i][i] = g[i][i] = 1;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (__gcd(a[i], a[j]) > 1)
                x[i][j] = 1;

    for (int i = n; i >= 1; i--)
    {
        for (int j = i; j <= n; j++)
        {
            int l = i, r = j;
            for (int k = l; k <= r; k++)
            {
                if (f[i][k] && g[k][j])
                {
                    if (l > 1 && x[l - 1][k])
                        g[l - 1][r] = 1;
                    if (r < n && x[r + 1][k])
                        f[l][r + 1] = 1;
                }
            }
        }
    }

    for (int i = 1; i <= n; i++)
    {
        if (f[1][i] && g[i][n])
        {
            cout << "Yes" << endl;
            return 0;
        }
    }
    cout << "No" << endl;
}
原文地址:https://www.cnblogs.com/mollnn/p/14354409.html