【JZOJ6287】扭动的树

本文探讨了一种基于键值排序的区间动态规划方法,通过预处理最大公约数(gcd)来优化状态转移,实现O(n^2log)的时间复杂度。详细介绍了如何通过枚举中转点进行状态转移,以及如何利用树形结构和子节点间的关系来构建最优解。

description


analysis

  • 区间DPDPDP,首先按照键值排个序,这样保证树的中序遍历就为原序列

  • f[0][i][j]f[0][i][j]f[0][i][j]表示[i..j][i..j][i..j]区间作为[unknown..i−1][unknown..i-1][unknown..i1]右儿子的最大和,f[1][i][j]f[1][i][j]f[1][i][j]就是[i..j][i..j][i..j]区间作为[j+1..unknown][j+1..unknown][j+1..unknown]左儿子

  • 预处理fff的初值是很明显的,然后O(n2log)O(n^2log)O(n2log)预处理出两两数之间的gcd⁡\gcdgcd

  • 对于一段区间[i..j][i..j][i..j],枚举中转点kkk,表示[i..k−1],[k+1,j][i..k-1],[k+1,j][i..k1],[k+1,j]分别作为kkk的左右儿子

  • k=ik=ik=ik=jk=jk=j特殊转移,i&lt;k&lt;ji&lt;k&lt;ji<k<j可知[i..j][i..j][i..j]可由f[1][i][k−1],f[0][k+1][j]f[1][i][k-1],f[0][k+1][j]f[1][i][k1],f[0][k+1][j]转移得到

  • 具体转移到000111取决于a[k]a[k]a[k]a[i−1],a[j+1]a[i-1],a[j+1]a[i1],a[j+1]是否符合条件(gcd⁡&gt;1\gcd&gt;1gcd>1


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define MAXN 305
#define INF 1000000007 
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)

using namespace std;

ll f[2][MAXN][MAXN];
ll g[MAXN][MAXN];
ll sum[MAXN];
ll n,ans=-INF;

struct node
{
	ll x,y;
}a[MAXN];

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
inline ll max(ll x,ll y){return x>y?x:y;}
inline bool cmp(node a,node b){return a.x<b.x;}
inline ll get(ll x,ll y){return sum[y]-sum[x-1];}
inline ll gcd(ll x,ll y){return x%y==0?y:gcd(y,x%y);}
int main()
{
	freopen("T2.in","r",stdin);
	//freopen("tree.in","r",stdin);
	//freopen("tree.out","w",stdout);
	n=read();
	fo(i,0,n)fo(j,0,n)f[0][i][j]=f[1][i][j]=-INF;
	fo(i,1,n)a[i].x=read(),a[i].y=read();
	sort(a+1,a+n+1,cmp);
	fo(i,1,n)fo(j,1,n)g[i][j]=gcd(a[i].x,a[j].x);
	fo(i,1,n)
	{
		sum[i]=sum[i-1]+a[i].y;
		if (i!=1 && g[i][i-1]>1)f[0][i][i]=a[i].y;
		if (i!=n && g[i][i+1]>1)f[1][i][i]=a[i].y;
	}
	fo(len,2,n)
	{
		fo(i,1,n-len+1)
		{
			ll j=i+len-1,tmp;
			fo(k,i,j)
			{
				if (k==i)tmp=f[0][i+1][j]+get(i,j);
				else if (k==j)tmp=f[1][i][j-1]+get(i,j);
				else tmp=f[1][i][k-1]+f[0][k+1][j]+get(i,j);
				if (i!=1 && g[k][i-1]>1)f[0][i][j]=max(f[0][i][j],tmp);
				if (j!=n && g[k][j+1]>1)f[1][i][j]=max(f[1][i][j],tmp);
				if (n==len)ans=max(ans,tmp);
			}
		}
	}
	printf("%lld\n",ans<0?-1ll:ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值