BZOJ 2809 [Apio2012]dispatching(斜堆+树形DP)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2809

【题目大意】

  给出一棵树,求出每个点有个权值,和一个乘算值,请选取一棵子树,
  并在这个子树上选择一些节点,使得根节点的乘算值乘上选取的节点数价值最大,
  并且权值和不超过给定的限制

【题解】

  我们在树上做dfs,计算每个点作为子树根节点时候的价值,
  维护可并的权值大根堆,自下而上合并,当发现权值和大于限制的时候pop根节点即可。

【代码】

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N=100010;
int x,n,m,cnt,tot;
vector<int> v[N];
LL ans,sum[N],size[N];
int C[N],L[N],root[N];
struct SHeap{
    int v[N],l[N],r[N];
    int merge(int x,int y){
        if(x==0||y==0)return x+y;
        if(v[x]<v[y])swap(x,y);
        r[x]=merge(r[x],y);
        swap(l[x],r[x]);
        return x;
    }void pop(int &x){x=merge(l[x],r[x]);}
    int top(int x){return v[x];}
}heap;
void dfs(int x){
    root[x]=++tot; heap.v[tot]=C[x];
    size[x]=1; sum[x]=C[x];
    for(int i=0;i<v[x].size();i++){
        dfs(v[x][i]);
        sum[x]+=sum[v[x][i]];
        size[x]+=size[v[x][i]];
        root[x]=heap.merge(root[x],root[v[x][i]]);
    }
    while(sum[x]>m){
        sum[x]-=heap.top(root[x]);
        heap.pop(root[x]);
        size[x]--;
    }ans=max(ans,size[x]*L[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&x,&C[i],&L[i]);
        v[x].push_back(i);
    }dfs(1);
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/bzoj2809.html