[洛谷P1552] [APIO2012]派遣(左偏树)

这道题是我做的左偏树的入门题,奈何还是看了zsy大佬的题解才能过,唉,我太弱了。
左偏树总结

Part 1 理解题目

很显然,通过管理关系的不断连边,最后连出来的肯定是一棵树,那么不难得出,当一个忍者作为管理者时,最优解一定是去除掉所有的较大工资的忍者,剩下的忍者符合费用要求时,答案是管理者的管理能力×剩下的忍者数量。并且我们可以推出,当一棵子数中的一棵小子树中去掉了一个忍者,那么那个忍者一定不会对当前的子树有答案贡献。

Part 2 解题思想

都理解了题目了,就很清楚了,我们在dfs过程中记录一下集合元素,并把此节点以下所有的子树全部合并入一个堆里(dfs过程中已经处理了,每个堆都是最优堆),那么开始弹元素,维护大根堆,一个个弹出最大值,直到刚好符合要求,此时答案就是堆中剩余元素的数量×当前节点的管理能力,取max。完啦!

Part 3 code

    #include<iostream>  
    #include<cstdlib>  
    #include<cstdio>  
    #include<cmath>  
    #include<cstring>  
    #include<iomanip>  
    #include<algorithm>  
    #include<ctime>  
    #include<queue>  
    #define rg register  
    #define lst long long  
    using namespace std;  
    #define ll long long  
    const int N = 100005;  
    struct edge{int to,next;}a[N];  
    int head[N],cnt;  
    int n,Master,ls[N],rs[N],dis[N];  
    ll m,C[N],L[N],sum[N],sz[N],ans;  
    ll gi()  
    {  
        ll x=0,w=1;char ch=getchar();  
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();  
        if (ch=='-') w=0,ch=getchar();  
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();  
        return w?x:-x;  
    }  
    void Link(int u,int v)  
    {  
        a[++cnt]=(edge){v,head[u]};  
        head[u]=cnt;  
    }  
    int Merge(int A,int B)  
    {  
        if (!A||!B) return A+B;  
        if (C[A]<C[B]) swap(A,B);  
        rs[A]=Merge(rs[A],B);  
        if (dis[ls[A]]<dis[rs[A]]) swap(ls[A],rs[A]);  
        dis[A]=dis[rs[A]]+1;  
        return A;  
    }  
    int Delete(int A)  
    {  
        return Merge(ls[A],rs[A]);  
    }  
    int dfs(int u)  
    {  
        int A=u,B;  
        sum[u]=C[u];sz[u]=1;  
        for (int e=head[u];e;e=a[e].next)  
        {  
            int v=a[e].to;  
            B=dfs(v);  
            A=Merge(A,B);  
            sum[u]+=sum[v];sz[u]+=sz[v];  
        }  
        while (sum[u]>m)  
        {  
            sum[u]-=C[A];sz[u]--;  
            A=Delete(A);  
        }  
        ans=max(ans,L[u]*sz[u]);  
        return A;  
    }  
    int main()  
    {  
        n=gi();m=gi();  
        for (int i=1;i<=n;i++)  
        {  
            int u=gi();  
            if (!u) Master=i;  
            else Link(u,i);  
            C[i]=gi();L[i]=gi();  
        }  
        dfs(Master);  
        printf("%lld",ans);  
        return 0;  
    } 




哪怕人间是炼狱,梦想永远是天堂
继续走下去吧,理想永远都年轻,花儿一定会再次盛开
原文地址:https://www.cnblogs.com/cjoierljl/p/8641019.html