【bzoj2809】[Apio2012]dispatching

= =这道题调了好多遍才过。
其实分析一下这道题还是蛮简单的。
首先建图的时候我们把每个忍者作为一个点,我们可以发现,每个忍者的入度都为1(除了master),所以我们对其进行dfs,可以保证每个点只被遍历到一遍。
其次,这道题是可以贪心的,一方面这道题的结果为忍者个数*领导力,所以我们可以选择薪酬尽量小的忍者,另一方面,在dfs树中处于上端的忍者可以选择所有下端的忍者,也就是说,我们可以维护一个大根堆,堆中含有下层搜索树中的所有点,每次插入此时正在遍历的点,然后弹出权值最大的点直到堆中所有权值之和小于总薪酬。
如上分析,所以应该用一个可并堆,于是使用左偏树,跑的很快,交了很多遍是因为一直wa发现最后自己的merge写错了QAQ.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
const int N=100010;
int tot=0,cnt=0,head[N],c[N],root[N];
int n,m,l[N];
long long ans=0,sum[N],size[N];
struct edge{
    int u,v,next;
}e[100010];
int add(int u,int v){
    e[++tot].u=u;
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot;
}
struct Lefttree{
    int dis[N],rc[N],lc[N],v[N];
    int merge(int x,int y)
    {   
        if (!x||!y)return x+y;
        int tmp;
        if (v[x]<v[y])tmp=x,x=y,y=tmp;
        rc[x]=merge(rc[x],y);
        if (!lc[x]||dis[rc[x]]>dis[lc[x]])tmp=rc[x],rc[x]=lc[x],lc[x]=tmp;
        if(rc[x])dis[x]=dis[rc[x]]+1;
        else dis[x]=0;
        return x;
    }
    void pop(int &x){
        x=merge(lc[x],rc[x]);
    }
    int top(int x){
        return v[x];
    }
}lt;
inline int F(){
    register int aa,bb;register char ch;
    while(ch=getchar(),ch!='-'&&(ch>'9'||ch<'0'));ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
    while(ch=getchar(),ch>='0'&&ch<='9')aa=(aa<<3)+(aa<<1)+ch-'0';return bb?aa:-aa;
}
void dfs(int u)
{
    size[u]=1;sum[u]=c[u];
    root[u]=++cnt;lt.v[cnt]=c[u];
    for (int i=head[u];i;i=e[i].next)
    {
        dfs(e[i].v);
        size[u]+=size[e[i].v];
        root[u]=lt.merge(root[u],root[e[i].v]);
        sum[u]+=sum[e[i].v];
    }   
    while(sum[u]>m&&size[u]>0)
    {
        sum[u]-=lt.top(root[u]);
        lt.pop(root[u]);size[u]--;
    }
    ans=max(ans,size[u]*l[u]*1ll);
}
int main()
{
    memset(sum,0,sizeof(sum));
    n=F();m=F();int v;
    for (int i=1;i<=n;i++)
    {
        v=F(),c[i]=F(),l[i]=F();
        add(v,i);
    }
    dfs(1);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值