题意
给你一棵树
每一个点有一个权值
ai
a
i
要给树染色,必须先染父亲再染儿子
如果这个点是第
i
i
个染色的,那么他的代价是
要求代价最小
题解
一开始想了一个鬼畜贪心,我不知道他为什么是错的。。但是我也不知道他为什么能对,十分尴尬。反正他WA了,那就不管哪个做法了。。
考虑,如果当前节点
i
i
,他的权值是最大的,那么我们肯定是选完他的父亲立刻选他
于是他和他的父亲顺序就决定了,他的父亲下一个就是他
于是我们可以把这两个点看作一个
新点的权值就是他们的平均数,表示每延后一次回产生多少代价
做次,你就会剩下一个点,然后就做完了。。
剩下一个点时,所有点染色的顺序就出来了
这题
n2
n
2
可以过,但其实写一个堆时可以优化到
nlogn
n
l
o
g
n
的
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=1005;
LL a[N];
LL f[N],g[N],h[N],nxt[N],lst[N];
LL fa[N];
struct qq
{
LL x,y,last;
}e[N*2];LL num,last[N];
void init (LL x,LL y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
LL findfa (LL x) {return f[x]==x?f[x]:f[x]=findfa(f[x]);}
void dfs (LL x)
{
for (LL u=last[x];u!=-1;u=e[u].last)
{
LL y=e[u].y;
if (fa[x]==y) continue;
fa[y]=x;dfs(y);
}
}
int main()
{
LL n,rt;
while (true)
{
num=0;memset(last,-1,sizeof(last));
scanf("%lld%lld",&n,&rt);
if (n+rt==0) break;
for (LL u=1;u<=n;u++) scanf("%lld",&a[u]);
for (LL u=1;u<n;u++)
{
LL x,y;
scanf("%lld%lld",&x,&y);
init(x,y);init(y,x);
}
fa[rt]=0;dfs(rt);
for (LL u=1;u<=n;u++) {f[u]=u;g[u]=a[u];h[u]=1;lst[u]=u;nxt[u]=0;}
LL x,y,id;//g[u]/h[u]>x/y --> g[u]*y>h[u]*x
for (LL u=1;u<n;u++)
{
x=0;y=1;
for (LL i=1;i<=n;i++)
{
LL xx=findfa(i);
if (xx==rt) continue;
if (g[xx]*y>h[xx]*x) {id=xx;x=g[xx];y=h[xx];}
}
//id和他的父亲合并
LL xx=findfa(fa[id]);
//printf("YES:%lld %lld\n",id,xx);
LL last=lst[xx];
lst[xx]=lst[id];nxt[last]=id;f[id]=f[xx];
h[xx]=h[xx]+h[id];
g[xx]=g[xx]+g[id];
}
LL now=rt,ans=0;
for (LL u=1;u<=n;u++)
{
//printf("now:%lld\n",now);
ans=ans+a[now]*u;
now=nxt[now];
}
printf("%lld\n",ans);
}
return 0;
}