POJ传送门
【题目分析】
这是什么神仙贪心。。。。。。
首先可以得到一个比较明显的性质,对于当前树中的最大点,在它的父亲被染色后一定会染它。
那么对于染色序列中的三个点x,y,z,如果已知x,y是连续染色,那么就有两种情况:
1)染色序列为x,y,z,代价为x+2y+3z
2)染色序列为z,x,y,代价为z+2x+3y
然后就是一个神仙操作,两边加上(z-y)再同时除以2,得到两个代价:
1)(x+y)/2+2z
2)z+(x+y/2)*2
所以就可以转化成点权为(x+y)/2和z的点的比较了,根据这个思路,得到解决方法:定义num[i]为当前点包含的点的个数,a[i]表示当前点权值总和,所以等效权值为a[i]/num[i],那么每次找到这个等效权值最大的点,把他和父亲合并即可。
【代码~】
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1010;
const int MAXM=2010;
int n,rt,cnt;
LL ans;
int a[MAXN],num[MAXN];
int head[MAXN],fa[MAXN];
int nxt[MAXM],to[MAXM];
int Read(){
int i=0,f=1;
char c=getchar();
for(;(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-') f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar()) i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void add(int x,int y){
++cnt;
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
}
void getfa(int u,int f){
for(int i=head[u];i!=-1;i=nxt[i]){
int v=to[i];
if(v==f) continue;
fa[v]=u;
getfa(v,u);
}
}
int main(){
while(scanf("%d%d",&n,&rt)!=EOF){
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;++i) a[i]=Read(),num[i]=1;
for(int i=1;i<n;++i){
int x=Read(),y=Read();
add(x,y),add(y,x);
}
fa[rt]=-1;
getfa(rt,-1);
for(int i=1;i<n;++i){
double maxx=0;
int id;
for(int j=1;j<=n;++j) if(1.0*a[j]/num[j]>=maxx&&j!=rt) maxx=1.0*a[j]/num[j],id=j;
for(int j=1;j<=n;++j) if(fa[j]==id) fa[j]=fa[id];
ans+=(LL)a[id]*num[fa[id]];
a[fa[id]]+=a[id];
a[id]=0;
num[fa[id]]+=num[id];
}
ans+=a[rt];
cout<<ans<<'\n';
}
}