题解
- 由于二叉搜索树的中序遍历是一定的,所以可以区间DP
- f[0/1][l][r]表示[l~r]的区间的根节点为l-1或r+1
- 然后先O(n^2)做出两两gcd,记录前缀和,就可以转移了
代码
1 #include <cstdio>
2 #include <iostream>
3 #include <algorithm>
4 #define N 310
5 #define ll long long
6 using namespace std;
7 struct node{ll k;ll v;}a[N];
8 ll n,ans=-1,p,sum[N],f[2][N][N];
9 ll b[N][N];
10 bool cmp(node a,node b) { return a.k<b.k; }
11 ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b); }
12 int main()
13 {
14 freopen("tree.in","r",stdin),freopen("tree.out","w",stdout),scanf("%lld",&n);
15 for(int i=0;i<=1;i++) for(int j=1;j<=300;j++) for(int k=1;k<=300;k++) f[i][j][k]=-0x7fffffffffffffll;
16 for (ll i=1;i<=n;i++) scanf("%lld%lld",&a[i].k,&a[i].v);
17 sort(a+1,a+n+1,cmp);
18 for (ll i=1;i<=n;i++) for (ll j=i+1;j<=n;j++) b[i][j]=b[j][i]=gcd(a[i].k,a[j].k);
19 for (ll i=1;i<=n;i++)
20 {
21 sum[i]=sum[i-1]+a[i].v;
22 if (i!=1&&b[i][i-1]!=1) f[0][i][i]=a[i].v;
23 if (i!=n&&b[i][i+1]!=1) f[1][i][i]=a[i].v;
24 }
25 for (ll i=2;i<=n;i++)
26 for (ll l=1,r;l+i-1<=n;l++)
27 {
28 r=l+i-1;
29 for (ll j=l;j<=r;j++)
30 {
31 if (j==l) p=f[0][l+1][r]+(sum[r]-sum[l-1]);
32 if (j==r) p=f[1][l][r-1]+(sum[r]-sum[l-1]);
33 if (l<j&&j<r) p=f[1][l][j-1]+f[0][j+1][r]+(sum[r]-sum[l-1]);
34 if (l!=1&&b[j][l-1]!=1) f[0][l][r]=max(f[0][l][r],p);
35 if (r!=n&&b[j][r+1]!=1) f[1][l][r]=max(f[1][l][r],p);
36 if (i==n) ans=max(ans,p);
37 }
38 }
39 printf("%lld",ans);
40 }