本题要看出性质并进行验证,程序难度低。(官方 Editorial 似乎没有写证明过程?难道是过于显而易见了吗…)
题意
给你一个数组 a a a,对于一棵 n n n 个节点的树 T T T, d i d_i di 为每个节点的度,定义 f ( T ) = ∑ i = 1 n d i 2 a i f(T)=\sum_{i=1}^{n}d_i^2a_i f(T)=∑i=1ndi2ai。求 f ( T ) f(T) f(T) 的最小值。
性质
式子:如果
∑
i
=
1
n
d
i
=
2
n
−
2
\sum_{i=1}^{n}d_i=2n-2
∑i=1ndi=2n−2,对于任意的
d
d
d,一定有合法的树满足条件。
证明:
- 首先先证明一棵树的度数和是 2 n − 2 2n-2 2n−2。 n n n 个节点的树有 n − 1 n-1 n−1 条边,每条边会被两头的节点算两次度,所以度数和是 2 ( n − 1 ) 2(n-1) 2(n−1)。
- 接着我们证明可以构造出这样一棵树满足条件。先将
d
d
d 数组从大到小排序,我们先满足
d
d
d 大的节点。举
n
=
10
n=10
n=10 的例子:
这样每次加完点后再在新的点上连边,直到后面的 d d d 都是 1 1 1,无需添加。在 d = 1 d=1 d=1 之前,每次都会加上至少一个叶子节点(除第一次是 d i d_i di 个之外,每次加 d i − 1 d_i-1 di−1 个叶子节点),保证不会没有上一次新加的点可以用来连新的边。最终的点数一定是 n n n,否则不满足度数和为 2 n − 2 2n-2 2n−2。算一下每次新增点数, ( d 1 + 1 ) + ( d 2 − 1 ) + ( d 3 − 1 ) . . . + ( d n − 1 ) = 2 n − 2 − ( n − 2 ) = n (d_1+1)+(d_2-1)+(d_3-1)...+(d_n-1)=2n-2-(n-2)=n (d1+1)+(d2−1)+(d3−1)...+(dn−1)=2n−2−(n−2)=n,也是对的。
思路
我们先将所有 d i d_i di 都设成 1 1 1,然后循环 n − 2 n-2 n−2 次,每次选择一个使 f ( T ) f(T) f(T) 增加量最小的 d i d_i di 进行加 1 1 1,这里可以用 p r i o r i t y _ q u e u e priority\_queue priority_queue 找到最小的。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
long long ans,a[200005],d[200005];
priority_queue<pair<long long,int>,vector<pair<long long,int> >,greater<pair<long long,int> > > q;
//first:如果将d[pos]加1会对f造成多少增加量 second:pos
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
d[i]=1;ans+=a[i];
q.push(make_pair(3*a[i],i));
}
for(int i=1;i<=n-2;i++){
pair<long long,int> p=q.top();
q.pop();
ans+=p.first;
d[p.second]++;
q.push(make_pair(((d[p.second]+1)*(d[p.second]+1)-d[p.second]*d[p.second])*a[p.second],p.second));
}
cout<<ans<<endl;
return 0;
}