【DP】Codeforces1025D Recovering BST

题意:

给出n个点,每个点有一个点权 ai a i
现在要求构造一颗二叉搜索树,需要满足:每条边两端的点其权值不互质。
询问能否满足


分析:

很水的区间DP题啦。。。
不知道为什么同学们还有WK大佬都没做出来呢。。。
看来同学们的DP训练还得再做做啊。。。

(话是这么说,不过我似乎除了DP啥也不会了。。。好菜啊。。。)

首先,由于其是一颗二叉搜索树,所以其中序遍历必然是不下降的。

所以呢,可以将序列中连续的某一段当做一颗子树来处理:
每次枚举一个根节点,将原序列分为左右两个序列(即左子树和右子树)。

然后发现,对任意一个区间 (l,r) ( l , r ) 来说,其上一层的父亲节点必然是 l1 l − 1 r+1 r + 1 (cch和wk大佬都是这里被卡了呢)。

证明很显然啦。。。如果其父亲不是 l1 l − 1 r+1 r + 1 的话。。。你打算把中间空着的那一段放哪?

如果还没懂的话我再扯详细一点:
根据DP转移方式,每次在区间中枚举一个点后,将区间分为左半部分和右半部分两个子区间,那么这两个子区间的父亲节点即为我们枚举出来的这个点,所以这两个区间就满足上述条件,对其子序列数学归纳一下就证明出来了。

所以dp定义就很显然啦。。。
dp(i,j,0) d p ( i , j , 0 ) 表示区间 [l,r] [ l , r ] 构成的子树是否有一种方案满足其根节点的值与 al1 a l − 1 不互质
dp(i,j,1) d p ( i , j , 1 ) 表示区间 [l,r] [ l , r ] 构成的子树是否有一种方案满足其根节点的值与 ar+1 a r + 1 不互质

转移就是枚举一个k (lkr) ( l ≤ k ≤ r )
如果 dp[l][k1]=1 d p [ l ] [ k − 1 ] = 1 dp[k+1][r]=1 d p [ k + 1 ] [ r ] = 1 gcd(ak,al1) g c d ( a k , a l − 1 ) ,则 dp[l][r][0]=1 d p [ l ] [ r ] [ 0 ] = 1 ;
如果 dp[l][k1]=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");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值