题意:
给出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<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#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");
}