「CF1025D Recovering BST」
郑州讲过的题了
发现这是一个二叉搜索树,给出的还是中序遍历,我们很自然的想到我们需要可以用一个\(f[i][j][k](k\in[i,j])\)来表示区间\([i,j]\)能不能形成以\(k\)为根的二叉搜索树
就是区间的\(dp\)的套路我们还需要枚举一下树根,复杂度高达\(O(n^5)\)
很不可行啊
换一个思路,我们用\(f[i][j][0/1]\)表示区间\([i,j]\)能否形成一棵左子树/右子树
如果形成的是左子树,自然树根是\(j+1\),如果是右子树,根自然是\(i-1\)
于是我们枚举区间\([i,j]\),枚举和\([i,j]\)形成一棵树的另一个区间,由于这个区间已经确定了左端点或者右端点,我们\(O(n^3)\)就能完成枚举
之后如果能拼接成一棵新树,树根自然也就知道了,转移过去就好了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL long long
#define re register
#define maxn 705
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int gcd(int a,int b) {if(!b) return a;return gcd(b,a%b);}
int n,a[maxn];
int e[maxn][maxn];
int f[maxn][maxn][2];
int main() {
n=read();
for(re int i=1;i<=n;i++) a[i]=read();
for(re int i=1;i<=n;i++)
for(re int j=i+1;j<=n;j++)
e[i][j]=e[j][i]=(gcd(a[i],a[j])!=1);
for(re int i=1;i<=n;i++) {
if(i>1&&e[i-1][i]) f[i][i][1]=1;
if(i<n&&e[i][i+1]) f[i][i][0]=1;
}
for(re int p=1;p<n;p++) {
for(re int i=1;i<=n;i++) {
int j=i+p-1;
if(f[i][j][1]) {
for(re int k=1;k<=i-2;k++)
if(f[k][i-2][0]) {
if(e[k-1][i-1]) f[k][j][1]=1;
if(e[j+1][i-1]) f[k][j][0]=1;
}
if(e[i-2][i-1]) f[i-1][j][1]=1;
if(e[i-1][j+1]) f[i-1][j][0]=1;
}
if(f[i][j][0]) {
for(re int k=j+2;k<=n;k++)
if(f[j+2][k][1]) {
if(e[j+1][k+1]) f[i][k][0]=1;
if(e[j+1][i-1]) f[i][k][1]=1;
}
if(e[j+1][j+2]) f[i][j+1][0]=1;
if(e[j+1][i-1]) f[i][j+1][1]=1;
}
}
}
f[1][0][0]=1;
int flag=0;
for(re int i=2;i<=n;i++)
if(f[i][n][1]&&f[1][i-2][0]) flag=1;
flag|=f[1][n-1][0];
puts(flag?"Yes":"No");
return 0;
}