[区间dp] Jzoj P6287 扭动的树
题解
- 由于二叉搜索树的中序遍历是一定的,所以可以区间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 }