大意:有向图给定n-1条边然后给出每个点的权值,每个点的权值只能累加一次,问从根节点走,可以走k次能累计的最大点权和。
思路:本题可以进行两遍dfs操作,第一次为从叶子节点到跟节点的节点权值和。然后根据权值排序,然后根据排序后的下标,来进行第二遍dfs搜索即从当前到根节点的和。
输出前k大值即可。
#include <iostream>
#include<cstring>
#include<stdio.h>
#define inf 0x3f3f3f3f
#include<algorithm>
#define LL long long
const int Ma = 100010;
using namespace std;
struct node{
LL w,to,next;
}q[Ma*5];
struct Node{
LL sum;
int id;
}Q[Ma];
LL arr[Ma],head[Ma*5],cnt,ans[Ma];
bool vis[Ma];
void Add(int a,int b){
q[cnt].to = b;
q[cnt].next = head[a];
head[a] = cnt++;
}
LL dfs1(int u){
if( vis[u] )
return Q[u].sum;
vis[u] = true;
Q[u].sum = arr[u];
for(int i = head[u];~i;i=q[i].next){
int v = q[i].to;
Q[u].sum += dfs1(v);
}
return Q[u].sum;
}
LL dfs2(int u){
if(vis[u]) return 0;
vis[u] = true;
LL tmp = arr[u];
for(int i = head[u];~i;i = q[i].next){
int v = q[i].to;
tmp += dfs2(v);
}
return tmp;
}
bool op(Node a,Node b){
return a.sum > b.sum;
}
bool cmp(LL a,LL b){
return a > b;
}
int main()
{
LL n,m,i,j,k,cla,a,b;
scanf("%I64d",&cla);
for(LL zu = 1;zu <= cla;++ zu){
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
cnt = 0;
scanf("%I64d%I64d",&n,&k);
for(i = 1;i <= n;++ i){
scanf("%I64d",&arr[i]);
Q[i].id = i;
}
for(i = 0;i < n-1;++ i){
scanf("%d%d",&a,&b);
Add(b,a);
}
for(i = 1;i <= n;++ i)
if(!vis[i])
dfs1(i);
sort(Q+1,Q+n+1,op);
memset(vis,false,sizeof(vis));
for(i = 1;i <= n;++ i){
ans[i] = dfs2(Q[i].id);
}
sort(ans+1,ans+n+1,cmp);
LL ed = 0;
for(i = 1;i <= k;++ i){
ed += ans[i];
}
printf("Case #%I64d: %I64d\n",zu,ed);
}
return 0;
}