HDU

来源:互联网 发布:安卓模拟器 for mac 编辑:程序博客网 时间:2024/06/09 15:30

题意:一棵树有n个结点,n-1条边,每个结点有个权值。每次可以获得从根节点走到叶子结点所有结点的权值和,但是每个结点的权值只能使用一次。求走k次所能获得的最大权值和。

思路:首先用dfs1求一遍叶子节点到根结点的权值。根据这个权值从大到小排序,从权值大的开始,dfs2,再求一遍叶子节点到根节点的权值,并且将这条链上除了叶子节点,其他节点的权值改为0,当遇到权值为0的点时,就返回。

将得到的新的权值从大到小排序,选前k个。

注:权值可能会爆int。

#include <cstdio>#include <algorithm>#include <iostream>#include<vector>#include<cmath>#include<set>#include<cstring>#include<sstream>#include<queue>#include<map>using namespace std;typedef long long ll;const int maxn = 100000 + 10;const int maxt = 100200;const int inf = 0x3f3f3f3f;const ll INF = 0x7f7f7f7f7f7f7f7f;const int mod = 1e9 + 7;const double pi = acos(-1.0);const double eps = 1e-8;int n, m, k;vector<int> g[maxn];struct Node{    ll x;    int id;    bool operator <(const Node& rhs) const{        return x > rhs.x;    }}a[maxn];int vis[maxn];ll d[maxn];void dfs1(int u){    if(vis[u]) return;    vis[u] = 1;    for(int i = 0; i < g[u].size(); ++i){        int v = g[u][i];        dfs1(v);        a[u].x += a[v].x;    }}void dfs2(int u){    if(d[u] == 0) return;    for(int i = 0; i < g[u].size(); ++i){        int v = g[u][i];        dfs2(v);        d[u] += d[v];        d[v] = 0;    }}bool cmp(ll x1, ll x2){    return x1 > x2;}int main(){    int T, kase = 0;    scanf("%d", &T);    while(T--){        scanf("%d%d", &n, &k);        for(int i = 1; i <= n; ++i){            scanf("%lld", &a[i].x);            d[i] = a[i].x;            a[i].id = i;        }        for(int i = 1; i <= n; ++i) g[i].clear();        for(int i = 0; i < n - 1; ++i){            int x, y;            scanf("%d%d", &x, &y);            g[y].push_back(x);        }        memset(vis, 0, sizeof vis);        for(int i = 1; i <= n; ++i)            if(!vis[i]) dfs1(i);        sort(a + 1, a + n + 1);        for(int i = 1; i <= n; ++i){            dfs2(a[i].id);        }        sort(d + 1, d + n + 1, cmp);        ll ans = 0;        for(int i = 1; i <= n && i <= k; ++i)            ans += d[i];        printf("Case #%d: %lld\n", ++kase, ans);    }}


1 0