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; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮

浙公网安备 33010602011771号