题意:
解法:
一个比较显然的做法是令d[i][j][k]表示[i,j]能否以k为根节点,
枚举[i,j],枚举k为根,那么左右子树分别为[i,k-1],[k+1,r],
左子树中枚举一个p,如果a[i][k-1][p]==1&&gcd(a[p],a[k])!=0,那么k可以作为左子树父节点.
右子树中枚举一个q,如果a[k+1][j][q]==1&&gcd(a[q],a[k])!=0,那么k可以作为右子树父节点.
如果左右子树都能找到连接点,那么d[i][j][k]=1.
但是这个做法的复杂度是O(n^4)的,显然不行,需要优化.
考虑二叉排序树性质,由于二叉排序树的中序遍历是有序的,
且对于二叉排序树的每一个节点,都满足左小右大.
那么对于[l,r],它的父节点一定是l-1或者r+1.
我们可以令d[i][j][0]表示[i,j]这个区间,i是能否作为i+1,j]的父节点,
令d[i][j][1]表示[i,j]这个区间,j能否作为[i,j-1]的父节点,
可以用O(n^3)的区间dp计算出d[i][j][0/1],
最后如果存在d[1][i][1]==1&&d[i][n][0]==1,那么满足题意.
code:
#include<bits/stdc++.h>
using namespace std;
int d[705][705][2];
int g[705][705];//g[i][j]=(gcd(a[i],a[j])!=1.
int a[705];
int n;
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=(__gcd(a[i],a[j])!=1);
}
}
for(int len=1;len<=n;len++){
for(int i=1;i<=n;i++){
int j=i+len-1;
if(j>n)break;
if(len==1){
d[i][j][0]=d[i][j][1]=1;
}else if(len==2){
d[i][j][0]=d[i][j][1]=g[i][j];
}else{
//以i为[i+1,j]的父节点
for(int k=i+1;k<=j;k++){
if(d[i+1][k][1]&&d[k][j][0]&&g[i][k]){
d[i][j][0]=1;break;
}
}
//以j为[i,j-1]的父节点
for(int k=i;k<=j-1;k++){
if(d[i][k][1]&&d[k][j-1][0]&&g[k][j]){
d[i][j][1]=1;break;
}
}
}
}
}
int ok=0;
for(int i=1;i<=n;i++){
if(d[1][i][1]&&d[i][n][0]){
ok=1;break;
}
}
if(ok){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
signed main(){
solve();
}