bzoj 3252 攻略(长链剖分 + 贪心)

SCOI2017原题

其实只是D1T1,但是……bzoj的那个标签是什么鬼?

现在bzoj没有标签了……

dfs序 + 线段树?

是不是有点太麻烦了

想一个简单的做法!

长链剖分!

当然这个长是广义上的长,我们以点权作为深度,记录一个点到叶子节点的最长距离,同时也以此作为剖分的条件,取代了一般的size,然后贪心,把所有剖出来的链,放进一个堆,或者说排序也可以,连续取最大的k个加起来即可,注意答案会爆int

#include <bits/stdc++.h>
using namespace std;
const int maxn = 300000 + 100;
inline int read() {
    int ch,  x = 0,  f = 1;ch = getchar();
    while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    ch == '-' ? f = -1,  ch = getchar() : 0;
    while(ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return f * x; 
}
int n, m;
int val[maxn];
struct Edge {
    int to, nxt;
    Edge() {}
    Edge(int _to, int _nxt):to(_to), nxt(_nxt) {}
}E[maxn << 1];
priority_queue<long long>Q;
int fa[maxn], son[maxn], top[maxn], h[maxn], cnt;
long long max_dep[maxn];
inline void add_edge(int u, int v) {
    E[++cnt] = Edge(v, h[u]), h[u] = cnt;
    E[++cnt] = Edge(u, h[v]), h[v] = cnt;
}
long long ans;
void dfs1(int x) {
    max_dep[x] = val[x]; son[x] = 0;
    for(int i = h[x]; ~i; i = E[i].nxt) {
        int to = E[i].to;
        if(to == fa[x]) continue;
        fa[to] = x;
        dfs1(to);
        max_dep[x] = max(max_dep[x], max_dep[to] + val[x]);
        if(max_dep[to] > max_dep[son[x]]) son[x] = to; 
    }
}
void dfs2(int x) {
    if(x == son[fa[x]]) top[x] = top[fa[x]];
    else top[x] = x;
    if(son[x]) dfs2(son[x]);
    for(int i = h[x]; ~i; i = E[i].nxt) {
        int to = E[i].to;
        if(to == son[x] || to == fa[x]) continue;
        dfs2(to); 
    }
}
signed main() {
    memset(h, -1, sizeof(h));
    n = read(), m = read();
    for(int i = 1; i <= n; i++) val[i] = read();
    for(int i = 1; i < n; i++) add_edge(read(), read());
    dfs1(1);
    dfs2(1);
    for(int i = 1; i <= n;i ++) {
        if(top[i] == i) Q.push(max_dep[i]);
    } 
    int j = 1;
    while(!Q.empty() && j <= m)
    {
        ans += Q.top();
        Q.pop();
        j++;
    }
    printf("%lld\n", ans);
    return 0;
} 


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值