题意:
给出n个点,每个点有一个点权aiai
现在要求构造一颗二叉搜索树,需要满足:每条边两端的点其权值不互质。
询问能否满足
分析:
很水的区间DP题啦。。。
不知道为什么同学们还有WK大佬都没做出来呢。。。
看来同学们的DP训练还得再做做啊。。。
(话是这么说,不过我似乎除了DP啥也不会了。。。好菜啊。。。)
首先,由于其是一颗二叉搜索树,所以其中序遍历必然是不下降的。
所以呢,可以将序列中连续的某一段当做一颗子树来处理:
每次枚举一个根节点,将原序列分为左右两个序列(即左子树和右子树)。
然后发现,对任意一个区间(l,r)(l,r)来说,其上一层的父亲节点必然是l−1l−1或r+1r+1(cch和wk大佬都是这里被卡了呢)。
证明很显然啦。。。如果其父亲不是l−1l−1或r+1r+1的话。。。你打算把中间空着的那一段放哪?
如果还没懂的话我再扯详细一点:
根据DP转移方式,每次在区间中枚举一个点后,将区间分为左半部分和右半部分两个子区间,那么这两个子区间的父亲节点即为我们枚举出来的这个点,所以这两个区间就满足上述条件,对其子序列数学归纳一下就证明出来了。
所以dp定义就很显然啦。。。
dp(i,j,0)dp(i,j,0)表示区间[l,r][l,r]构成的子树是否有一种方案满足其根节点的值与al−1al−1不互质
dp(i,j,1)dp(i,j,1)表示区间[l,r][l,r]构成的子树是否有一种方案满足其根节点的值与ar+1ar+1不互质
转移就是枚举一个k(l≤k≤r)(l≤k≤r),
如果dp[l][k−1]=1dp[l][k−1]=1且dp[k+1][r]=1dp[k+1][r]=1且gcd(ak,al−1)gcd(ak,al−1),则dp[l][r][0]=1dp[l][r][0]=1;
如果dp[l][k−1]=1dp[l][k−1]=1且dp[k+1][r]=1dp[k+1][r]=1且gcd(ak,ar+1)gcd(ak,ar+1),则dp[l][r][1]=1dp[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");
}
本文介绍了一种使用区间动态规划方法解决特定二叉搜索树构建问题的技术。该方法通过枚举根节点并确保每条边两端的点权值不互质来构建树。文章详细解释了状态定义、转移方程,并提供了完整的代码实现。
212

被折叠的 条评论
为什么被折叠?



