题目大意:
在给定带权值节点的树上从1开始不回头走到某个底端点后得到所有经过的点的权值后,这些点权值修改为0,到达底部后重新回到1,继续走,问走k次,最多能得到多少权值之和
这其实就是相当于每一次都走权值最大的那一条路径,进行贪心k次
首先先来想想树链剖分的时候的思想:
重儿子表示这个儿子对应的子树的节点数最多,那么每次访问都优先访问重儿子
这道题里面我们进行一下转化,如果当前儿子能走出一条最长的路径,我们就令其为重儿子,那么很容易想到,到达父亲时,如果选择重儿子,那么之前到达
父亲所得的权值一定是记录在重儿子这条路径上的,那么访问轻儿子的时候,因为前面的值在到达重儿子后修改为0,所以走到轻儿子之前权值和修改为0
我们将所有到达底端点的路径长度保存到rec数组中,将rec排序取前k个即可,如果不够取,相当于全部取完,因为后面再走也就是相当于0,不必计算
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100105;
int head[maxn],nxt[maxn],tot,to[maxn];
void add(int u,int v)
{
to[tot] = v;nxt[tot] = head[u];head[u] = tot++;
//cout << tot << " " << v << endl;
}
int heavyson[maxn];
long long val[maxn],siz[maxn];
long long an[maxn];
int t = 0;
void dfs(int u)
{
//cout << "dfs" << u << endl;
//siz[u] = val[u];
long long maxval=-1;
for(int i=head[u];~i;i=nxt[i])
{
int v = to[i];
dfs(v);
if(siz[v]>maxval) {maxval=siz[v];heavyson[u] = v;}
// cout << siz[v] << " siz " << v <<" " << heavyson[u]<< endl;
//siz[u] += siz[v];
}
if(maxval>=0) siz[u] = maxval + val[u];
else siz[u] = val[u];
}
void dfs1(int u,long long cost)
{
// cout << cost << " cost " <<heavyson[u] << endl;
bool flag = true;
if(heavyson[u])
{
flag = false;
dfs1(heavyson[u],cost+val[heavyson[u]]);
}
for(int i=head[u];~i;i=nxt[i])
{
int v = to[i];
if(v==heavyson[u]) continue;
dfs1(v,val[v]);
flag = false;
}
if(flag) an[t++] = cost;
}
bool cmp(const long long a,const long long b)
{
return a>b;
}
int main()
{
int T;
cin >> T;
for(int cas = 1;cas<=T;cas++)
{
tot = 0;
memset(head,-1,sizeof(head));
int n,k;
int u,v;
cin >> n >> k;
for(int i=1;i<=n;i++) scanf("%I64d",val+i);
for(int i=1;i<n;i++)
{
scanf("%d %d",&u,&v); add(u,v);
}
t=0; memset(heavyson,0,sizeof(heavyson));
dfs(1);
dfs1(1,val[1]);
sort(an,an+t,cmp);
long long ans = 0;
for(int i=0;i<t;i++)
{
if(i==k) break;
ans += an[i];
//cout << an[i] << " an" <<endl;
}
printf("Case #%d: ",cas); cout << ans << endl;
}
return 0;
}