1025D
给出升序排列的n个数,问这些数能否构成一棵二叉搜索树。要求每条边所连的两个点的gcd大于1。
赛场上一眼看出是个图论题把所有在环上的边去掉然后判断剩下的边是否存在不合法的边……
然而并不是。
给出的n个数是升序排列疯狂暗示区间dp……考虑用dp[rt][l][r]表示区间[l,r]以rt为根是否有解,然后就需要枚举区间以及rt还有左右子区间的根,总的复杂度是O(n^4),是不能接受的。
骚一下。
把dp[rt][l][r]变成:dp[i][rt-1][0]和dp[rt+1][r][1],其中dp[i][j][0]表示[i,j]是j+1的左子树,dp[i][j][1]表示[i,j]是i-1的右子树。
这样在O(n^3)的时间里可以处理完毕。
orz
/*ргргрг*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1205;
const ll INF = 1LL << 63 - 1;
const double eps = 1e-8;
int n, a[maxn], g[maxn][maxn];
int dp[maxn][maxn][2];
int gcd(int a, int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
int main()
{
scanf("%d", &n);
for(int i = 1;i <= n;i++)
scanf("%d", &a[i]);
memset(dp, 0, sizeof(dp));
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(i == j) continue;
g[i][j] = (gcd(a[i], a[j]) != 1);
}
}
for(int i = 1;i <= n;i++) dp[i][i][0] = dp[i][i][1] = 1;
for(int len = 1;len <= n;len++)
{
for(int i = 1;i <= n;i++)
{
int j = i + len - 1;
if(j > n) continue;
for(int k = i + 1;k <= j;k++)
dp[i][j][0] |= (g[i][k] && dp[i + 1][k][1] && dp[k][j][0]);
for(int k = i;k < j;k++)
dp[i][j][1] |= (g[k][j] && dp[i][k][1] && dp[k][j - 1][0]);
}
}
for(int i = 1;i <= n;i++)
{
if(dp[1][i][1] && dp[i][n][0])
{
printf("Yes\n");
return 0;
}
}
printf("No\n");
return 0;
}