HDU 5242 Game (树上贪心|类 树链剖分)

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

你可能感兴趣的:(贪心,树链剖分)