题解--重构

· · 题解

10pts

你爆搜出这棵树应该都可以过。

30pts

由 prufer 序列我们得知,对于一个度数的序列 d,如果 \forall 1\le d_i<n,\sum d_i=2n-2。那么就一定可以生成至少一棵树。
所以我们只需要构造出合法的 d 序列,就可以求得最大值。
于是我们可以写出一个 O(n^3) 的 dp:设 f_{i,j} 为前 i 个点放了 j 的度数的最小代价。dp 转移方程:f_{i,j}=\min\limits_{k=i-1}^{j-1}f_{i-1,k}+(j-k)^2v_i

50pts

对上面那个式子进行斜率优化。

100pts

首先我们对于每个点先放一个度数,消除 $\forall 1\le d_i<n,\sum d_i=2n-2$ 的限制。然后把 $((d_i+1)^2-d_i^2)v_i$ 当成费用丢进堆里,一直选,每一次将选出来的 $d_i$ 加 $1$,更新贡献,选 $n-1$ 次就可以。 一个口胡的证明:如果当前你选了一个更劣的解,它解锁出来的一定也是更劣的解,所以不如就选当前最优解。 代码很好实现。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=3e5+5; struct que { int w,v; que(int W,int V) { w=W,v=V; } friend bool operator<(que a,que b) { return (2*a.w+1)*a.v>(2*b.w+1)*b.v;//按照增加的费用排序 } }; int n,v[maxn]; ll ans; priority_queue<que> q; int main() { scanf("%d",&n); for(register int i=1; i<=n; i++)scanf("%d",&v[i]),ans+=v[i],q.push(que(1,v[i])); for(register int i=1; i<n-1; i++) { que u=q.top(); q.pop(); ans+=(2*u.w+1)*u.v; if(u.w<n-1)q.push(que(u.w+1,u.v)); } printf("%lld",ans); return 0; } ```