[GCD][区间DP]JZOJ 6287 扭动的树
分析
比较显然的是,某个子树一定是一段键值连续的区间
所以就是个区间DP,合并时判断是否满足GCD条件即可
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const ll Inf=1ll<<62; const int N=310; struct Node { ll k,v; }a[N]; ll f[2][N][N],s[N],ans; bool g[N][N]; int n; ll GCD(ll a,ll b) {return !a?b:GCD(b%a,a);} bool CMP(Node a,Node b) {return a.k<b.k;} int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld%lld",&a[i].k,&a[i].v); sort(a+1,a+n+1,CMP); for (int i=1;i<=n;i++) { s[i]=s[i-1]+a[i].v; for (int j=1;j<=n;j++) g[i][j]=GCD(a[i].k,a[j].k)>1ll?1:0; } for (int k=0;k<2;k++) for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) f[k][i][j]=-Inf; for (int i=2;i<=n;i++) if (g[i][i-1]) f[1][i][i]=a[i].v; for (int i=1;i<n;i++) if (g[i][i+1]) f[0][i][i]=a[i].v; for (int len=1;len<n;len++) for (int l=1,r=l+len;r<=n;l++,r++) for (int j=l;j<=r;j++) { ll t=0; if (j==l) t=f[1][l+1][r]; else if (j==r) t=f[0][l][r-1]; else t=f[0][l][j-1]+f[1][j+1][r]; if (l>1&&g[j][l-1]) f[1][l][r]=max(f[1][l][r],t+s[r]-s[l-1]); if (r<n&&g[j][r+1]) f[0][l][r]=max(f[0][l][r],t+s[r]-s[l-1]); } ans=f[1][2][n]; for (int i=2;i<n;i++) ans=max(ans,f[0][1][i-1]+f[1][i+1][n]); ans=max(ans,f[0][1][n-1]);ans+=s[n]; if (ans>=0) printf("%lld",ans); else printf("-1"); }
在日渐沉没的世界里,我发现了你。