BZOJ-2809-dispatching(枚举+dfs序建主席树)

题目
题意:已知条件:人数,上线薪水m,每一个人都有一个上级,薪水,领导能力。
找出一个人,从他所掌管的所有员工里随意选出几个员工,但这些员工的总薪水要<=m,求这个人的领导能力*员工个数的最大值。
在这里插入图片描述
思路:枚举+dfs序建主席树

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=1e5+5,M=N;
int ea[N],ab[N],refl[N],cnt;
int in[N],out[N],fp[N],ovo;
int head[N],tot;
struct tree{int l,r,num;}t[N*50];
int root[N],sz;ll sum[N*50];
struct Edge{int to,nex;}edge[N];
void add(int from,int to)
{
    edge[++tot]=(Edge){to,head[from]};head[from]=tot;
}
void dfs(int x)
{
    in[x]=ovo++,fp[ovo-1]=x;
    for(int i=head[x];i;i=edge[i].nex)
        dfs(edge[i].to);
    out[x]=ovo-1;
}
void build(int wh,int val,int &x,int y,int l,int r)
{
    x=++sz,t[x]=t[y],t[x].num++,sum[x]=sum[y]+val;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    if(wh<=mid)
        build(wh,val,t[x].l,t[y].l,l,mid);
    else
        build(wh,val,t[x].r,t[y].r,mid+1,r);
}
int query(int x,int y,int k,int l,int r)//一定要注意是右子树-左子树!!!!一定不能马虎T_T
{
    if(l==r)
    {
        ll summ=sum[y]-sum[x];int numm=t[y].num-t[x].num;
        if(summ<=k)
            return numm;
        else //这些numm个不可以完全返回,只能返回的个数总sum要<=k;
            return k/(summ/numm);
    }
    ll left=sum[t[y].l]-sum[t[x].l];
    int mid=(l+r)>>1;
    if(left<=k)//权值线段树中的左子树的总sum(薪水)<=k,询问右子树薪水不超过k-left的最大num+左子树的所有num(员工个数)
        return t[t[y].l].num-t[t[x].l].num+query(t[x].r,t[y].r,k-left,mid+1,r);
    else
        return query(t[x].l,t[y].l,k,l,mid);
}
int main()
{
//    m(head,0),tot=0,ovo=1;
    int n,m,master=1;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int fa;scanf("%d%d%d",&fa,&ea[i],&ab[i]);
        add(fa,i);//因为每个节点的fa编号小于自己 所以master=1,建立单向边即可,dfs序也不需(vis标记是否为已访问过的其父/或者用fa[]判断)
        refl[i]=ea[i];
    }
    sort(refl+1,refl+n+1);
    cnt=unique(refl+1,refl+n+1)-(refl+1);
    ovo=1;
    dfs(master);--ovo;//(ovo==n)
    for(int i=1;i<=ovo;i++)//根据dfs之后新序列建树。
    {
        int x=fp[i],dex=lower_bound(refl+1,refl+cnt+1,ea[x])-refl;//x是指dfs序中第i个对应原序列的第几个
        build(dex,ea[x],root[i],root[i-1],1,cnt);
    }
    ll maxn=0;
    for(int i=1;i<=n;i++)
    {
        ll ans=(ll)ab[i]*query(root[in[i]-1],root[out[i]],m,1,cnt);//根据[in[i],out[i]] dfs序查询。注意(ll)。
        maxn=max(maxn,ans);
    }
    printf("%lld\n",maxn);
}

猜你喜欢

转载自blog.csdn.net/qq_42576687/article/details/90137071
今日推荐