[区间dp] Jzoj P6287 扭动的树

Description

 

题解

  • 由于二叉搜索树的中序遍历是一定的,所以可以区间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 }

 

posted @ 2019-08-09 21:36  BEYang_Z  阅读(265)  评论(0编辑  收藏  举报