HDU 5242 利用树链剖分思想进行贪心

题目大意:

在给定带权值节点的树上从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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值