正题
题目大意
一颗二叉查找树,以keyikey_ikeyi为建值,以pip_ipi为价值。然后一个节点的sumsumsum定义为这棵子树的价值之和。
要求相邻两个节点不互质的情况下所有节点的最大sumsumsum值之和。
解题思路
二叉查找树满足中序遍历的建值从小到大,所以我们考虑区间dpdpdp。
每次将两个区间组成的二叉树合并在一起。
我们预处理出哪些节点可以相邻
设f0/1,i,jf_{0/1,i,j}f0/1,i,j表示作为将作为左边/右边的子树与上将上一个合并过来(根为i/ji/ji/j且没有另一边子树)时的最大价值。
那么我们可以得出动态转移方程
f0,i,j=f1,i,k,+f0,k+1,j(vi−1,k)f_{0,i,j}=f_{1,i,k},+f_{0,k+1,j}(v_{i-1,k})f0,i,j=f1,i,k,+f0,k+1,j(vi−1,k)
f1,i,j=f1,i,k,+f0,k+1,j(vk,j+1)f_{1,i,j}=f_{1,i,k},+f_{0,k+1,j}(v_{k,j+1})f1,i,j=f1,i,k,+f0,k+1,j(vk,j+1)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=310;
struct node{ll k,p;
}a[N];
ll n,f[2][N][N],ans,s[N];
bool v[N][N];
bool cmp(node x,node y)
{return x.k<y.k;}
int main()
{//freopen("tree.in","r",stdin);//freopen("tree.out","w",stdout);scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld%lld",&a[i].k,&a[i].p);for(int i=1;i<=300;i++)for(int j=1;j<=300;j++)for(int k=0;k<2;k++)f[k][i][j]=-0x7fffffffffffffll;sort(a+1,a+1+n,cmp);ans=-1e18;for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++) v[i][j]=(__gcd(a[i].k,a[j].k)==1);for(ll i=1;i<=n;i++){s[i]=s[i-1]+a[i].p;if(i!=1&&!v[i][i-1]) f[0][i][i]=a[i].p;if(i!=n&&!v[i][i+1]) f[1][i][i]=a[i].p;}for(ll l=2;l<=n;l++){for(ll i=1;i<=n-l+1;i++){ll j=i+l-1;for(ll k=i;k<=j;k++){ll b;if(k==i) b=f[0][i+1][j]+(s[j]-s[i-1]);if(k==j) b=f[1][i][j-1]+(s[j]-s[i-1]);if(i<k&&j>k) b=f[1][i][k-1]+f[0][k+1][j]+(s[j]-s[i-1]);if(i!=1&&!v[k][i-1]) f[0][i][j]=max(f[0][i][j],b);if(i!=n&&!v[k][j+1]) f[1][i][j]=max(f[1][i][j],b);if(l==n) ans=max(ans,b);}}}if(ans<0) printf("-1");else printf("%lld",ans);
}