\(\text{Description}\)
\(\text{Solution}\)
暴力枚举是不可行的。(搜索大佬随手切题)
发现复杂度这么高是因为我们枚举了左边界,右边界,根节点,根节点的 \(2\) 个儿子。
这个时候我们就要用到二叉搜索树的一个性质:根节点大于左子树,小于右子树。
我们枚举一段区间并制定其为左子树或右子树,这样我们就能确定根。然后判断能否成立即可。
最后枚举根就行了。
\(\text{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;
}