大意:有向图给定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; }