Loading

BZOJ 3252 攻略(长链剖分模板 链的常用性质 贪心)

BZOJ 3252 攻略

​ 给定一棵带边权的树,选择 k 个叶子结点,使这些叶子结点与根节点相连,形成 k 条路径。输出被路径覆盖的所有边的边权和的最大值(同一条边若被重复覆盖只算一次)。

思路:

​ 很容易看出,是选择 k 条链。我们可以贪心的想一想,一定是权值和最大的链先被选中,然后选删去这条链后权值最大的链。那就是一个模拟删链的过程,这就是树链剖分的经典应用。我们进行长链剖分,但是这里的长链不是对深度剖分,是对权值剖分。将每条链塞进优先队列里,取前 k 个即可。

代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long 
void read(int &x) {int s = 0, f = 1; char ch = getchar(); while(!isdigit(ch)) {f = (ch == '-' ? -1 : f); ch = getchar();} while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();} x = s * f;}
const int N = 1000005;

int n, k;
vector<int> g[N];
ll val[N];
priority_queue<ll> Q;

int father[N], hson[N];
ll mx_val[N];
void dfs1_init(int u, int fa)
{
    father[u] = fa;
    hson[u] = -1;
    for(int v : g[u])
    {
        if(v == fa) continue;
        dfs1_init(v, u);
        if(hson[u] == -1 || mx_val[v] > mx_val[hson[u]])
            hson[u] = v;
    }
    mx_val[u] = val[u] + mx_val[hson[u]];
}
void dfs2_init(int u, int head)
{
    if(u == head)
        Q.push(mx_val[u]);
    if(hson[u] != -1)
    {
        dfs2_init(hson[u], head);
        for(int v : g[u])
        {
            if(v == father[u] || v == hson[u])
                continue;
            dfs2_init(v, v);
        }
    }
}

void init() 
{
    for(int i = 1; i <= n; i ++)
        g[i].clear();
}

void solve()
{
    read(n); read(k);
    init();
    for(int i = 1; i <= n; i ++)
        scanf("%lld", &val[i]);
    for(int i = 1; i < n; i ++)
    {
        int x, y;
        read(x); read(y);
        g[x].push_back(y);
        g[y].push_back(x);
    }

    dfs1_init(1, 0);
    dfs2_init(1, 1);

    ll res = 0;
    while(Q.size() && k)
        res += Q.top(), Q.pop(), k --;
    printf("%lld\n", res);
}

int main()
{
    int _ = 1;
    while(_--)
        solve();
}
posted @ 2022-12-22 22:10  DM11  阅读(16)  评论(0编辑  收藏  举报