题目大意:
分析:
因为这颗二叉查找树的中序遍历必定是
k
e
y
key
key值的升序排列,
考虑dp,
设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示选了区间
[
i
,
j
]
[i,j]
[i,j]上的一个点作为
k
k
k儿子时的最大合法sum和。
显然对于一个区间
[
l
,
r
]
[l,r]
[l,r]而言,不管我选出一个什么样的点去作为其父亲的儿子,
都不会影响
f
l
,
r
,
k
f_{l,r,k}
fl,r,k
因为代表
[
l
,
r
]
[l,r]
[l,r]这个区间的我选出来的儿子,必定是和父亲互质的,且它的父亲是唯一的,
比如
[
l
,
r
]
[l,r]
[l,r]区间我选出来的节点假如是作为右儿子,它的父亲必定是
l
−
1
l-1
l−1这个点,
作为左儿子同理
那么我们用dfs去遍历一遍所有情况即可
注意用
f
i
,
j
,
k
f_{i,j,k}
fi,j,k去判断重复出现的情况
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define N 305
using namespace std;
typedef long long ll;
struct Node {
ll k, v;
}a[N];
int sum[N][N], ok[N][N], n;
ll f[N][N][2];
bool cmp(Node a, Node b) {
return a.k < b.k;
}
ll Get_gcd(ll a, ll b) {
return b ? Get_gcd(b, a % b) : a;
}
ll dfs(int l, int r, int mark) {
if (l > r) return 0;
if (f[l][r][mark]) return f[l][r][mark];
ll now = 0; int fa;
bool check = 0;
if (!mark) fa = l - 1; else fa = r + 1;
rep(i, l, r) {
if (!ok[fa][i]) continue;
ll ls = dfs(l, i - 1, 1);
ll rs = dfs(i + 1, r, 0);
if (ls == -1 || rs == -1) continue;
now = max(now, ls + rs);
check = 1;
}
if (!check) return (f[l][r][mark] = - 1);
return (f[l][r][mark] = now + sum[l][r]);
}
int main(){
freopen("tree.in", "r", stdin);
freopen("tree.out","w",stdout);
scanf("%d", &n);
rep(i, 1, n) scanf("%lld %lld", &a[i].k, &a[i].v);
sort(a + 1, a + n + 1, cmp);
rep(i, 1, n)
rep(j, i, n) sum[i][j] = sum[i][j - 1] + a[j].v;
rep(i, 1, n) {
ok[0][i] = 1;
rep(j, i + 1, n)
if (Get_gcd(a[i].k, a[j].k) != 1) ok[i][j] = ok[j][i] = 1;
}
printf("%lld\n", dfs(1, n, 0));
}