description
analysis
区间\(DP\),首先按照键值排个序,这样保证树的中序遍历就为原序列
设\(f[0][i][j]\)表示\([i..j]\)区间作为\([unknown..i-1]\)的右儿子的最大和,\(f[1][i][j]\)就是\([i..j]\)区间作为\([j+1..unknown]\)的左儿子
预处理\(f\)的初值是很明显的,然后\(O(n^2log)\)预处理出两两数之间的\(\gcd\)
对于一段区间\([i..j]\),枚举中转点\(k\),表示\([i..k-1],[k+1,j]\)分别作为\(k\)的左右儿子
\(k=i\)或\(k=j\)特殊转移,\(i<k<j\)可知\([i..j]\)可由\(f[1][i][k-1],f[0][k+1][j]\)转移得到
具体转移到\(0\)或\(1\)取决于\(a[k]\)与\(a[i-1],a[j+1]\)是否符合条件(\(\gcd>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; }