bzoj3011

可并堆

复习一下可并堆

维护一个大跟堆,每次把节点儿子打上边权标记,然后合并,可并堆上维护一个size,每次把大于l的弹出,size就是答案

程序中那个d写l和r速度差不多,我写l是表示右儿子到u的最长距离

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
struct edge {
    int to;
    long long w;
    edge(int to = 0, long long w = 0) : to(to), w(w) {}
};
int n;
long long L;
long long dis[N], tag[N];
int root[N], l[N], r[N], d[N], ans[N], size[N];
vector<edge> G[N];
void pushdown(int x)
{
    if(tag[x] == 0) return;
    tag[l[x]] += tag[x];
    dis[l[x]] += tag[x];
    tag[r[x]] += tag[x];
    dis[r[x]] += tag[x];
    tag[x] = 0; 
}
int merge(int u, int v)
{
    if(!u) return v;
    if(!v) return u;
    pushdown(u);
    pushdown(v);
    if(dis[u] < dis[v]) swap(u, v);
    r[u] = merge(r[u], v);
    if(d[r[u]] > d[l[u]]) swap(l[u], r[u]);
    d[u] = d[l[u]] + 1;
    size[u] = size[l[u]] + size[r[u]] + 1;
    return u;
}
void dfs(int u)
{
    root[u] = u;
    size[u] = 1;
    for(int i = 0; i < G[u].size(); ++i)
    {
        edge e = G[u][i];
        dfs(e.to);
        tag[root[e.to]] += e.w;
        dis[root[e.to]] += e.w;
        root[u] = merge(root[u], root[e.to]);    
    }
    while(dis[root[u]] > L && root[u]) root[u] = merge(l[root[u]], r[root[u]]);
    ans[u] = size[root[u]];
}
int main()
{
    scanf("%d%lld", &n, &L);
    for(int i = 2; i <= n; ++i)
    {
        int u;
        long long len;
        scanf("%d%lld", &u, &len);
        G[u].push_back(edge(i, len));
    }
    dfs(1);
    for(int i = 1; i <= n; ++i) printf("%d
", ans[i]);
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/19992147orz/p/7354033.html