题解 洛谷 P1552 【[APIO2012]派遣】

根据题意,我们不难发现忍者之间的关系是树形结构。

发现答案的统计只是在该节点的子树中,因此我们考虑通过树形\(DP\)来解决问题。

从叶子节点开始,从下往上考虑,因为一个节点的最优答案只与他的领导力和在子树中选了几个点有关,与选哪些点无关,所以我们要最大化选点的个数。

贪心策略即为尽可能的多选点,当选出的点的薪水超过预算时,删去当前选出的点中薪水最大的点,通过这样的策略来保证我们能选出最多的点。

通过可并堆(左偏树)来实现,同时维护一些信息,选出点的薪水总和\(sum\),选出点的个数\(siz\)

其他的一些细节就看代码吧,统计答案记得开\(long\ long\)

\(code:\)

#include<bits/stdc++.h>
#define maxn 200010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
ll n,m,root,ans;
ll fa[maxn],ls[maxn],rs[maxn],dis[maxn],val[maxn],l[maxn],sum[maxn],siz[maxn];
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]=(edge){to,head[from]};
    head[from]=edge_cnt;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int merge(int x,int y)
{
    if(x==y) return 0;
    if(!x||!y) return x+y;
    if(val[x]<val[y]) swap(x,y);
    rs[x]=merge(rs[x],y),fa[rs[x]]=x;
    if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
    if(rs[x]) dis[x]=dis[rs[x]]+1;
    else dis[x]=0;
    return x;
}
void del(int x)
{
    fa[ls[x]]=ls[x],fa[rs[x]]=rs[x];
    fa[x]=merge(ls[x],rs[x]);
}
void dfs(int x)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        dfs(y);
        siz[x]+=siz[y];
        sum[x]+=sum[y];
        merge(find(x),find(y));
    }
    while(sum[x]>m)
    {
        int rootx=find(x);
        siz[x]--;
        sum[x]-=val[rootx];
        del(rootx);
    }
    ans=max(ans,l[x]*siz[x]);
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;++i)
    {
        int fath;
        read(fath),read(val[i]),read(l[i]);
        sum[i]=val[i],siz[i]=1,fa[i]=i;
        if(fath) add(fath,i);
        else root=i;
    }
    dfs(root);
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lhm-/p/12229824.html