返回顶部

Codeforces Round #505 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final) D. Recovering BST (dp,二叉搜索树)

  • 题意:给你\(n\)个升序的点,问你是否能构造一颗二叉搜索树,且每个儿子节点和父节点的\(gcd>1\).

  • 题解:首先可以预处理每两个点之间的\(gcd\),我们先考虑暴力的写法,设\(dp[l][r][k]\)表示区间\([l,r]\)\(k\)为根节点是否合法,那么如果\(dp[l][r]k]\)要合法的话,其左儿子\(dp[l][k-1][k_1]\),和右儿子\(dp[k+1][r][k_2]\)合法即可,那么我们要在区间dp\(O(n^3)\)的基础上再跑一个循环,复杂度为\(O(n^4)\),空间时间双双爆表.

    这里就要用到二叉搜索树的一个性质,因为它的左儿子比父亲小,右儿子比父亲大,所以中序遍历得到的点权一定是升序的.那么假如一个父亲的左子树的区间为\([l,r]\),那么父亲节点一定为\(r+1\),如果是右子树的话,就是\(l-1\).

    \(L[i][k]\)表示以\(k\)为根,区间\([i,k-1]\)做左子树是否合法,\(R[k][j]\)表示以\(k\)为根,区间\([k+1,r]\)做右子树是否合法.如果以\(k\)为根,且它的左右子树均合法的话,我们就可以利用上述性质,当前区间\([i,j]\)可以就可以作为以\(i-1\)为根的右子树,以\(j+1\)为根的左子树,这样就完成转移了.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
     
    int n;
    int a[N];
    int g[705][705];
    int L[705][705],R[705][705];
     
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    	cin>>n;
    	rep(i,1,n) cin>>a[i],L[i][i]=R[i][i]=1;
     
    	rep(i,1,n){
    		rep(j,1,n){
    			if(gcd(a[i],a[j])!=1){
    				g[i][j]=g[j][i]=1;
    			}
    		}
    	}
     
    	for(int len=0;len<n;++len){
    		for(int i=1;i+len<=n;++i){
    			int j=i+len;
    			for(int k=i;k<=j;++k){
    				if((L[i][k]) && (R[k][j])){
    					if(i==1 && j==n){
    						cout<<"Yes\n";
    						return 0;
    					}
    					if(g[i-1][k]) R[i-1][j]=1;
    					if(g[k][j+1]) L[i][j+1]=1;
    				}
    			}
    		}
    	}
     
    	cout<<"No\n";
     
        return 0;
    }
    
posted @ 2021-04-29 17:34  _Kolibri  阅读(50)  评论(0)    收藏  举报