题面
一颗树有 n 个节点,这些节点被标号为:1,2,3…n,每个节点 i 都有一个权值 A[i]。
现在要把这棵树的节点全部染色,染色的规则是:
根节点R可以随时被染色;对于其他节点,在被染色之前它的父亲节点必须已经染上了色。
每次染色的代价为T*A[i],其中T代表当前是第几次染色。
求把这棵树染色的最小总代价。
输入格式
第一行包含两个整数 n 和 R ,分别代表树的节点数以及根节点的序号。
第二行包含 n 个整数,代表所有节点的权值,第 i 个数即为第 i 个节点的权值 A[i]。
接下来n-1行,每行包含两个整数 a 和 b ,代表两个节点的序号,两节点满足关系: a 节点是 b 节点的父节点。
除根节点外的其他 n-1 个节点的父节点和它们本身会在这 n-1 行中表示出来。
同一行内的数用空格隔开。
输出格式
输出一个整数,代表把这棵树染色的最小总代价。
数据范围
1≤n≤1000,
1≤A[i]≤1000
输入样例:
5 1
1 2 1 2 4
1 2
1 3
2 4
3 5
输出样例:
33
思路
太难....是这样的,我们首先考虑没有树形结构的情况,我们应该如何染色,对的,我们当然想到排序不等式,先染权值大的。那么加了树的结构限制说明了什么,说明了我们在给一个点染色之前,我们必须先给它的父亲染色,所以当我们沿着原来的思路去想的时候,就会发现,如果这个时候我们想要染一个权值最大的点,就必须先去满足他的父亲。那么我们其实可以把a和b看成一个节点一起染色,那么问题最后一定会变成,这一对点和另外一个点的大小关系,当我们考虑两种情况并且做差比较的时候可以发现,如果我们要先染点对,那么符合的条件是c小于点对的均值,拓展到点对和点对直接也是一样。基于这样的思路,我们每次找出点的平均值最大的点,把他并入父亲,更新ans并且同时打上lazy tag,最后在n-1次的更新后,得到最后的ans。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1010;
int n,root;
struct node {
int father,sum;
int size;
double avg;
}a[maxn];
inline int find () {
double avg=0;
int ans=-1;
for (int i=1;i<=n;i++) {
if (a[i].avg>avg&&i!=root) {
ans=i;
avg=a[i].avg;
}
}
return ans;
}
int main () {
int ans=0;
cin>>n>>root;
for (int i=1;i<=n;i++) {
node &nd=a[i];
nd.size=1;
cin>>nd.sum;
nd.avg=nd.sum;
ans+=nd.sum;
}
for (int i=1;i<=n;i++) {
int x,y;
cin>>x>>y;
a[y].father=x;
}
for (int i=0;i<n-1;i++) {
int ver=find ();
int f=a[ver].father;
ans+=a[ver].sum*a[f].size;
a[ver].avg=-1;
for (int j=1;j<=n;j++) {
if (a[j].father==ver) a[j].father=f;
}
a[f].size+=a[ver].size;
a[f].sum+=a[ver].sum;
a[f].avg= (double ) a[f].sum/a[f].size;
}
cout<<ans<<endl;
return 0;
}