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\n",ans); return 0; }
愿你出走半生,归来仍是少年