C17 左偏树 P1552 [APIO2012] 派遣


#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=100005; int n,m; int fa[N],pay[N],lead[N]; //原树节点信息 int ls[N],rs[N],dis[N],siz[N],root[N]; //左偏树 long long sum[N],ans; int merge(int x,int y){ if(!x||!y) return x+y; if(pay[x]<pay[y]) swap(x,y); //大根堆 rs[x]=merge(rs[x],y); if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]); dis[x]=dis[rs[x]]+1; return x; } int main(){ scanf("%d%d",&n,&m); dis[0]=-1; for(int i=1; i<=n; i++){ scanf("%d%d%d",&fa[i],&pay[i],&lead[i]); root[i]=i; //每个节点看成一个单独的堆 siz[i]=1; sum[i]=pay[i]; ans=max(ans,1ll*lead[i]);//选一个节点的贡献 } for(int i=n; i>1; i--){ //从叶子节点向上枚举 int f=fa[i]; root[f]=merge(root[i],root[f]); siz[f]+=siz[i]; sum[f]+=sum[i];
while(sum[f]>m){ //循环删除堆中点权大的节点 siz[f]--; sum[f]-=pay[root[f]]; root[f]=merge(ls[root[f]],rs[root[f]]); } ans=max(ans, 1ll*lead[f]*siz[f]); } cout<<ans; }
// 左偏树+树形DP #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=100005; int n,m; int pay[N],lead[N]; //原树节点信息 int ls[N],rs[N],dis[N],siz[N],root[N]; //左偏树 long long sum[N],ans; int h[N],to[N],ne[N],tot; void add(int a, int b){ to[++tot]=b;ne[tot]=h[a];h[a]=tot; } int merge(int x,int y){ if(!x||!y) return x+y; if(pay[x]<pay[y]) swap(x,y); //大根堆 rs[x]=merge(rs[x],y); if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]); dis[x]=dis[rs[x]]+1; return x; } void dfs(int x){ siz[x]=1,sum[x]=pay[x]; for(int i=h[x]; i; i=ne[i]){ int y=to[i]; dfs(y); root[x]=merge(root[x],root[y]); siz[x]+=siz[y]; sum[x]+=sum[y]; } while(sum[x]>m){ siz[x]--; sum[x]-=pay[root[x]]; root[x]=merge(ls[root[x]],rs[root[x]]); } ans=max(ans,1ll*lead[x]*siz[x]); } int main(){ scanf("%d%d",&n,&m); dis[0]=-1; int rt; for(int i=1,f; i<=n; ++i){ scanf("%d%d%d",&f,&pay[i],&lead[i]); if(f) add(f,i); else rt=i; root[i]=i; //每个节点看成一个单独的堆 } dfs(rt); cout<<ans; }
练习:
浙公网安备 33010602011771号