CodeForces

( ext{Description})

传送门

( ext{Solution})

暴力枚举是不可行的。(搜索大佬随手切题

发现复杂度这么高是因为我们枚举了左边界,右边界,根节点,根节点的 (2) 个儿子。

这个时候我们就要用到二叉搜索树的一个性质:根节点大于左子树,小于右子树。

我们枚举一段区间并制定其为左子树或右子树,这样我们就能确定根。然后判断能否成立即可。

最后枚举根就行了。

( ext{Code})

#include<cstdio>

const int N = 702;
int n, a[N];
bool arr[N][N], l[N][N], r[N][N];//l与r数组:i到j区间可否成为左/右子树

int read() {
	int x = 0, f = 1; char s;
	while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
	while(s >= '0' && s <= '9') {
		x = (x << 1) + (x << 3) + (s ^ 48);
		s = getchar();
	}
	return x * f;
}

int gcd(const int a, const int b) {
	if(! b) return a;
	return gcd(b, a % b);
}

int main() {
	int j;
	n = read();
	for(int i = 1; i <= n; ++ i) a[i] = read();
	for(int i = 1; i <= n; ++ i)
		for(int j = 1; j <= n; ++ j)
			arr[i][j] = (gcd(a[i], a[j]) == 1 ? 0 : 1);
	for(int i = 1; i <= n; ++ i) l[i][i] = r[i][i] = 1;
	for(int dis = 1; dis < n; ++ dis)
		for(int i = 1; i <= n - dis; ++ i) {
			j = i + dis;
			for(int k = i; k < j; ++ k) l[i][j] |= (arr[k][j] & l[i][k] & r[k][j - 1]);
			for(int k = i + 1; k <= j; ++ k) r[i][j] |= (arr[i][k] & l[i + 1][k] & r[k][j]);
		}
	for(int i = 1; i <= n; ++ i)
		if(l[1][i] & r[i][n]) {
			puts("Yes");
			return 0;
		}
	puts("No");
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/14055372.html