好神的一道题
每一步在可以被染色的点选权值最大的点是错误想法,很容易(hack)
正确性质:树中除根节点以外最大权值的点,一定会在它父亲节点后立即染色
合并这两个节点,得到节点为两个权值的平均值
假设三个点
权值(x,y,z), (x,y)连续进行操作,就有两种染色方案
先染(x,y) ,代价(x+2y+3z) 先染(z),代价(z+2z+3y)
考虑两个代价的大小关系,把两个式子都加上((z-y))再除以二
得到(1.(x+y)/2+2z) (2.z+2((x+y)/2))
所以合并两个点是正确的,
等效权值:原始点权值总和/该点包含的原始点数
struct node{int f,siz,val;}a[N];
int n,R,fa[N]; bool vis[N];
priority_queue<pair<double,int> >q;
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
int main(){
while(~scanf("%d%d",&n,&R)){
int ans = 0,u,v;
for(int i = 1;i <= n;++i){
scanf("%d",&a[i].val);
a[i].siz = 1; ans += a[i].val;
fa[i] = i;vis[i] = 0;
if(i == R) continue;
q.push(make_pair(a[i].val,i));
}
for(int i = 1;i < n;++i){
scanf("%d%d",&u,&v);
a[v].f = u;
}
while(!q.empty()){
int p = q.top().second;
q.pop();
if(vis[p]) continue;
vis[p] = 1;
int f = find(a[p].f);
ans += a[f].siz * a[p].val;
a[f].val += a[p].val;
a[f].siz += a[p].siz;
if(f != R) q.push(make_pair((double)a[f].val/a[f].siz,f));
fa[p]=f;
}
printf("%d
",ans);
}
}