[题解] [CF1025D] Recovering BST

题面

题解

如果不是二叉搜索树其实这题就是最小生成树板子题
(f[l][r][k])([l, r]) 这段区间是否可以以 (k) 为根构成一棵合法的树
转移就是

[f[l][r][k] |= f[l][mid][i] & f[mid + 1][r][j], mid in [l, r - 1] ]

这个方程是 (O(n^5)) 的, 考虑优化
考虑到这样一个性质, 我们还是以 ([l, r]) 中根为 (k) 为例
我们只需要使 ([l, k - 1]) , ([k + 1, r]) 这两段区间可以构成合法的一棵树
并且这两棵树的树根可以和 (k) 相连就可以了
(g[l][r])([l, r]) 这一段区间是否可以以 (r) 为根构成一棵合法的树
(h[l][r])([l, r]) 这一段区间是否可以以 (l) 为根构成一棵合法的树
那么 (f[l][r][k] = 1) 就是 (g[l][k] = 1), (h[k][r] = 1)
那么我们就将求 (f) 转化为了求 (g, h)
(g) 的转移方程又是

[g[l][r] |= g[l][k] & h[k][r - 1] ]

(k)([l, r - 1]) 中且 (k) 可以与 (r) 连边
(h) 的转移方程类似, 这里就不再赘述了
那么这样答案就是 (O(n^3)) 的了
答案合法只需要 ([1, n]) 中存在一个 (i) 使得 (g[1][i] = h[i][n] = 1)

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 705; 
using namespace std;

int n, a[N];
bool f[N][N], g[N][N], vis[N][N]; 

template < typename T >
inline T read()
{
	T x = 0, w = 1; char c = getchar();
	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * w; 
}

int gcd(int n, int m) { return m ? gcd(m, n % m) : n; }

int main()
{
	n = read <int> ();
	for(int i = 1; i <= n; i++)
		a[i] = read <int> ();
	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(i != j) vis[i][j] = gcd(a[i], a[j]) != 1; 
	for(int r, k = 2; k <= n; k++)
		for(int l = 1; l + k - 1 <= n; l++)
		{
			r = l + k - 1;
			for(int p = l; p < r; p++)
				if(vis[p][r]) f[l][r] |= f[l][p] & g[p][r - 1];
			for(int p = r; p > l; p--)
				if(vis[l][p]) g[l][r] |= f[l + 1][p] & g[p][r]; 
		}
	for(int i = 1; i <= n; i++)
		if(f[1][i] & g[i][n]) { puts("Yes"); return 0; }
	puts("No"); 
	return 0; 
}

一点小小的收获

有的时候可以把一个式子拆成两部分, 对两部分分开 DP
因为拆开之后某一个值不需要再枚举了, 所以可以优化复杂度

原文地址:https://www.cnblogs.com/ztlztl/p/12790643.html