给出n个点,每个点有一个点权 ai a i
现在要求构造一颗二叉搜索树,需要满足:每条边两端的点其权值不互质。
询问能否满足
很水的区间DP题啦。。。
不知道为什么同学们还有WK大佬都没做出来呢。。。
看来同学们的DP训练还得再做做啊。。。
(话是这么说,不过我似乎除了DP啥也不会了。。。好菜啊。。。)
首先,由于其是一颗二叉搜索树,所以其中序遍历必然是不下降的。
所以呢,可以将序列中连续的某一段当做一颗子树来处理:
每次枚举一个根节点,将原序列分为左右两个序列(即左子树和右子树)。
然后发现,对任意一个区间 (l,r) ( l , r ) 来说,其上一层的父亲节点必然是 l−1 l − 1 或 r+1 r + 1 (cch和wk大佬都是这里被卡了呢)。
证明很显然啦。。。如果其父亲不是 l−1 l − 1 或 r+1 r + 1 的话。。。你打算把中间空着的那一段放哪?
如果还没懂的话我再扯详细一点:
根据DP转移方式,每次在区间中枚举一个点后,将区间分为左半部分和右半部分两个子区间,那么这两个子区间的父亲节点即为我们枚举出来的这个点,所以这两个区间就满足上述条件,对其子序列数学归纳一下就证明出来了。
所以dp定义就很显然啦。。。
dp(i,j,0) d p ( i , j , 0 ) 表示区间 [l,r] [ l , r ] 构成的子树是否有一种方案满足其根节点的值与 al−1 a l − 1 不互质
dp(i,j,1) d p ( i , j , 1 ) 表示区间 [l,r] [ l , r ] 构成的子树是否有一种方案满足其根节点的值与 ar+1 a r + 1 不互质
转移就是枚举一个k (l≤k≤r) ( l ≤ k ≤ r ) ,
如果 dp[l][k−1]=1 d p [ l ] [ k − 1 ] = 1 且 dp[k+1][r]=1 d p [ k + 1 ] [ r ] = 1 且 gcd(ak,al−1) g c d ( a k , a l − 1 ) ,则 dp[l][r][0]=1 d p [ l ] [ r ] [ 0 ] = 1 ;
如果 dp[l][k−1]=1 d p [ l ] [ k − 1 ] = 1 且 dp[k+1][r]=1 d p [ k + 1 ] [ r ] = 1 且 gcd(ak,ar+1) g c d ( a k , a r + 1 ) ,则 dp[l][r][1]=1 d p [ l ] [ r ] [ 1 ] = 1
额。。为了不卡常需要预处理一下每两个数的gcd。。然后就做完了。。。
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 710
using namespace std;
int a[MAXN],n;
bool dp[MAXN][MAXN][2];
int g[MAXN][MAXN];
int gcd(int x,int y){
if(y==0)
return x;
return gcd(y,x%y);
}
int main(){
SF("%d",&n);
for(int i=1;i<=n;i++)
SF("%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]);
for(int len=1;len<=n;len++)
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<=r;k++)
if((k==l||dp[l][k-1][1])&&(k==r||dp[k+1][r][0])){
if(l!=1&&g[k][l-1]!=1)
dp[l][r][0]=1;
if(r!=n&&g[k][r+1]!=1)
dp[l][r][1]=1;
}
}
bool ans=0;
for(int rt=1;rt<=n;rt++)
if((rt==1||dp[1][rt-1][1])&&(rt==n||dp[rt+1][n][0]))
ans=1;
if(ans==0)
PF("No\n");
else
PF("Yes\n");
}