题目链接:https://codeforces.com/problemset/problem/1025/D
题目大意:
给n个数字,问是否能构成一棵每条边连接的两个数最大公因数不为1的二叉搜索树, n ≤ 700 n≤700 n≤700
题目思路:
菜鸡理解错了二叉搜索树。。对于二叉搜索树上的点,右子树所有的点都比该点大,左子树所有的点都比该点小。还有一个性质,如果题目能够构成二叉搜索树,那么其中的每一个序列都能形成二叉搜索树,也就是所谓的最优子结构。 L [ i ] [ k ] L[i][k] L[i][k]表示以k为根的子树,左子树蔓延到i是否成立, R [ k ] [ i ] R[k][i] R[k][i]表示以k为根的子树,右子树蔓延到i是否成立。所以如果两个都为1的话,就是以k为根的子树,左边最远能到第几个数字,右边最远能到第几个数字。如果存在一个k满足最左到1,最右到n,那就是能构成,否则不能。怎么转移呢?区间dp惯用套路,最外围枚举长度,第二层枚举l r,第三层枚举根节点k。如果 L [ l ] [ k ] L[l][k] L[l][k]和 R [ k ] [ r ] R[k][r] R[k][r]都成立,那以k为根,最左到l最右到r的子树成立,那么就可以尝试转移,如果 a [ k ] a[k] a[k]和 a [ r + 1 ] a[r+1] a[r+1]的gcd不为1,那么就说明可以连接。这个时候r+1就成了根,r+1跟k相连,k所表示的子树成了它的左子树,所以更新 L [ l ] [ r + 1 ] = 1 L[l][r+1]=1 L[l][r+1]=1,另一边就同理。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN =700+5;
const int MAXM = 4e7+5;
const int MOD = 998244353;
int a[MAXN],n;
bool L[MAXN][MAXN],R[MAXN][MAXN],f[MAXN][MAXN];
int main()
{
while(~scanf("%d",&n)){
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(f,0,sizeof(f));
rep(i,1,n)scanf("%d",&a[i]),L[i][i]=R[i][i]=1;
rep(i,1,n){
rep(j,i+1,n){
if(__gcd(a[i],a[j])!=1)f[i][j]=f[j][i]=1;
}
}
rep(len,1,n){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
rep(k,l,r){
if(L[l][k]&&R[k][r]){
if(f[l-1][k])R[l-1][r]=1;
if(f[r+1][k])L[l][r+1]=1;
}
}
}
}
int flag=0;
rep(i,1,n){
if(L[1][i]&&R[i][n]){
flag=1;
break;
}
}
if(flag)puts("Yes");
else puts("No");
}
}