一、题目
二、解法
考虑暴力做法,设表示作为的左儿子右儿子是否合法,但是这种做法在n<=700
的数据中会MLE
。
考虑压缩无用的状态,由于我们需要构造的是一颗二叉搜索树,所以一段区间如果作为左儿子,那么根一定是,并且一段区间是否合法是可以通过枚举根节点来判断的,于是我们可以这么定义状态:
- 表示作为的左端点是否合法
- 表示作为的右端点是否合法
然后就可以愉快的转移了,我们左端点从大到小,右端点从小到大,来枚举一个区间,我们首先判断这个区间是否合法,枚举根(从来判断),然后看和能不能接到这个根上,以此来更新状态,初始值,我们在转移过程中如果被判定为合法,那么就输出Yes
,否则输出No
。
时间复杂度,贴个代码。
#include <cstdio>
const int M = 705;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,a[M],f[M][M],L[M][M],R[M][M];
int gcd(int a,int b)
{
return !b?a:gcd(b,a%b);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),L[i][i]=R[i][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(gcd(a[i],a[j])!=1)
f[i][j]=1;
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
for(int k=i;k<=j;k++)
if(L[i][k]&&R[k][j])
{
if(i==1 && j==n) {puts("Yes");return 0;}
if(f[i-1][k]) R[i-1][j]=1;
if(f[k][j+1]) L[i][j+1]=1;
}
puts("No");
}
来源:CSDN
作者:C202044zxy
链接:https://blog.csdn.net/C202044zxy/article/details/104133047