bzoj3252 攻略


题目简述:树版[k取方格数]
众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。
今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
“为什么你还没玩就知道每个场景的价值呢?”
“我已经看到结局了。”
Input
第一行两个正整数n,k
第二行n个正整数,表示每个场景的价值
以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
保证场景1为根节点 对于100%的数据,n<=200000,1<=场景价值<=2^31-1
Output
输出一个整数表示答案
Sample Input
5 2
4 3 2 1 1
1 2
1 5
2 3
2 4

Sol:找出每个点的最长链放入堆中(注意权值是在点上面的),每次找出最长链,并从堆中删除

 

#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 200005;
struct edges 
{
    int next, to;
    edges() {}
    edges(int _n, int _t) : next(_n), to(_t) {}
} e[N];
struct data 
{
    ll first;
    int w;
    data() {}
    data(ll _f, int _w) : first(_f), w(_w) {}
     
    inline bool operator < (const data &b) const 
	{
        return first < b.first;
    }
};
 
int n, k;
int first[N], tot;
int v[N];
bool vis[N];
ll mx[N], ans;
priority_queue <data> h;
inline int read() 
{
    int x = 0, sgn = 1;
    char ch = getchar();
    while (ch < '0' || '9' < ch) 
	{
        if (ch == '-') sgn = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') 
	{
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return sgn * x;
}
 
inline void add_edge(int x, int y) 
{
    e[++tot] = edges(first[x], y);
    first[x] = tot;
}
 
void dfs(int p) 
{
    int x, y;
    for (x = first[p]; x; x = e[x].next)
        dfs(y = e[x].to), mx[p] = max(mx[p], mx[y]);
    mx[p] += v[p];
    h.push(data(mx[p], p));
    //找出每个点的最长链,放到堆中 
}
 
void del(int p) 
{
    int x, y;
    vis[p] = 1;//将其打上删除标记,在堆中并没有删除之
    for (x = first[p]; x; x = e[x].next) 
        if (mx[y = e[x].to] == mx[p] - v[p]) 
		{
            del(y);
            break;
        }
}
 
int main() 
{
    int i, p, x, y;
    n = read(), k = read();
    for (i = 1; i <= n; ++i) v[i] = read();
    for (i = 1; i < n; ++i) 
	{
        x = read(), y = read();
        add_edge(x, y);
    }
    dfs(1);
    for (; k && !h.empty(); --k) 
	{
        while (vis[h.top().w] && !h.empty()) 
        //如果这个点删除过了,并且堆并不为空时 
		    h.pop();
        if (h.empty())
		    break;
        ans += mx[p = h.top().w];
        del(p);
    }
    printf("%lld\n", ans);
    return 0;
}

  

posted @ 2019-10-15 17:42  我微笑不代表我快乐  阅读(142)  评论(0编辑  收藏  举报