[APIO2012] 派遣

题意

给定一个m和一棵树,树上每个点有两个权值w,c。

需要选择一个节点r,然后从这个节点的子树中选择一些点,这些点满足如下条件:

$ \sum{w_i} \leq m $

并且使 $ c_r \times select\_node\_num $ 最大

节点数 $ n\leq 100,000$

解法

暴力的想法就是枚举节点,然后把所有子节点按权值排序,然后取尽可能多的点就好了
然后稍微一想,其实可以先从叶子节点做起,然后一层一层的往上合并
但是难点在于没办法快速把每个子树的序列合并起来,如果这个树是二叉树,那用归并的话,只要让序列的和不超过m,然后一直合并,复杂度应该是可以保证的,但是这个题的子树可以很多,因此时间复杂度应该会爆
反向思考一下,整个大根堆,当堆内元素的和大于m的时候就pop,答案就是堆的大小和根的权值乘积,那么为了快速合并,使用可并堆就好了

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn (100000+5)
#define mod 1000000007
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
    return x*f;
}
ll readll(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
    return x*f;
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int power(int x,int y){
    int t=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)t=t*x%mod;
    return t;
}
int n;
ll m;
ll c[maxn];

vector<vector<int> > edge(maxn, vector<int>(0));

struct Node{
    int ch[2],d;
    int size;
    ll val,sum;
} t[maxn];

int& rs(int x){return t[x].ch[t[t[x].ch[1]].d < t[t[x].ch[0]].d];}

int merge(int x,int y){
    if(!x || !y) return x|y;
    if(t[x].val > t[y].val) swap(x,y);
    rs(x) = merge(rs(x), y);
    t[x].sum = t[t[x].ch[0]].sum + t[t[x].ch[1]].sum + t[x].val;
    t[x].size = 1 + t[t[x].ch[0]].size + t[t[x].ch[1]].size;
    t[x].d = t[rs(x)].d + 1;
    return x;
}

int pop(int x){
    return merge(t[x].ch[0], t[x].ch[1]);
}

ll ans = 0;

int dfs(int x){
    if(edge[x].size() == 0){
        ans = max(ans, -t[x].val>m?0:c[x]);
    }
    int treeRoot = x;
    for(int i=0;i<edge[x].size();i++){
        treeRoot = merge(dfs(edge[x][i]), treeRoot);
        while(-t[treeRoot].sum > m){
            treeRoot = pop(treeRoot);
        }
        ans = max(ans, c[x]*t[treeRoot].size);
    }
    return treeRoot;
}

void solve(){
    n=read();m=readll();
    int root=0;
    for(int i=1;i<=n;i++){
        int fa = read();
        t[i].val = t[i].sum = -readll();
        t[i].size=1;
        c[i]=readll();
        if(!fa){
            root = i;
        }
        else{
            edge[fa].push_back(i);
        }
    }
    dfs(root);
    cout<<ans<<endl;
}
int main(){
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    solve();
    return 0;
}
posted @ 2022-07-29 16:23  HTWX  阅读(28)  评论(0编辑  收藏  举报