APIO2012 dispatching(dfs序+主席树)

来源:互联网 发布:淘宝内衣向大大 编辑:程序博客网 时间:2024/05/18 01:16

Description
在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为 Master。除了 Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者 发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递 人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。写一个程序,给定每一个忍者 i的上级 Bi,薪水Ci,领导力L i,以及支付给忍者们的薪水总预算 M,输出在预算内满足上述要求时顾客满意度的最大值。

1 ≤N ≤ 100,000 忍者的个数;

1 ≤M ≤ 1,000,000,000 薪水总预算;

0 ≤Bi < i 忍者的上级的编号;

1 ≤Ci ≤ M 忍者的薪水;

1 ≤Li ≤ 1,000,000,000 忍者的领导力水平。

Input Format
从标准输入读入数据。

第一行包含两个整数 N和 M,其中 N表示忍者的个数,M表示薪水的总预算。

接下来 N行描述忍者们的上级、薪水以及领导力。其中的第 i 行包含三个整 Bi , C i , L i分别表示第i个忍者的上级,薪水以及领导力。Master满足B i = 0,并且每一个忍者的老板的编号一定小于自己的编号 Bi < i。

Output Format
输出一个数,表示在预算内顾客的满意度的最大值。

Sample Input
5 4

0 3 3

1 3 5

2 2 2

1 2 4

2 3 1
Sample Output
6
Hint
样例解释:如果我们选择编号为 1的忍者作为管理者并且派遣第三个和第四个忍者,薪水总和为 4,没有超过总预算 4。因为派遣了 2 个忍者并且管理者的领导力为 3,

用户的满意度为 2 ,是可以得到的用户满意度的最大值。


思路:
这题有许多种方法:分块+莫队,贪心+可并树,dfs序+主席树。
我打过后两种方法,但是今天要介绍的是最后一种方法。
这道题本质是求在一个树上,以某个子树父节点的领导力*k(可支付子节点的数量)的最大值是多少,很明显这个k是前k小的子节点最好,于是使用主席树解决这个k值,其他就枚举。
首先dfs序是一种可以让树的节点有连续编号的一种方法,一个节点的进入时间为l,出去时间为r,则其所有子节点的进出时间都会在其区间内。
之后依靠dfs序,在树上做主席树,在该节点的进入时间和出去时间的主席树上都插入该值,因为插入了两次,树上便有两个,在算答案的时候,m要乘2,k要除以2。

#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<cstdio>typedef long long LL;#define N 200000LL ld[N],m,ans;LL wz[N],c[N*19],az[N*2],su[N*19];int root[N*19],tot,ls[N*19],rs[N*19],L[N*2],R[N*2],tou[N*2],n,dd,RT,dfx,s;struct aa{    int p,next;}da[N+30];struct pz{    LL po,v;}poi[N];void add(int x,int y){    da[++dd].p=y;da[dd].next=tou[x];tou[x]=dd;}void dfs(LL x){    L[x]=++dfx;    az[dfx]=x;    for (int i=tou[x];i;i=da[i].next)dfs(da[i].p);    R[x]=++dfx;    az[dfx]=x;}int update(int rt,LL po,LL va){    int tmp,rt1;tmp=rt1=++tot;    int l=1;int r=s;    su[rt1]=su[rt]+va;    c[rt1]=c[rt]+1;    while (l<r){        int mid=(l+r)/2;        if (po<=mid){            ls[rt1]=++tot;rs[rt1]=rs[rt];            rt1=ls[rt1];rt=ls[rt];            r=mid;        }else {            rs[rt1]=++tot;ls[rt1]=ls[rt];            rt1=rs[rt1];rt=rs[rt];            l=mid+1;        }        su[rt1]=su[rt]+va;        c[rt1]=c[rt]+1;    }    return tmp;}LL query(int rt,LL rt1,LL vaz){    LL tmp=0;    int l=1;int r=s;    while (l<r){        int mid=(l+r)/2;        LL xl=su[ls[rt1]]-su[ls[rt]];        if (xl<=vaz){            tmp=tmp+c[ls[rt1]]-c[ls[rt]];            vaz=vaz-xl;            rt=rs[rt];rt1=rs[rt1];            l=mid+1;        } else{            rt=ls[rt];rt1=ls[rt1];            r=mid;        }    }    return tmp;}bool cmp(pz A,pz B){return A.v<B.v;}int build(int l,int r){    int rt=++tot;    if (l==r)return rt;    int mid=(l+r)/2;    ls[rt]=build(l,mid);    rs[rt]=build(mid+1,r);    return rt;}int main(){    scanf("%d%lld",&n,&m);    for (int i=1;i<=n;i++){        int fa;        scanf("%d%lld%lld",&fa,&poi[i].v,&ld[i]);        poi[i].po=i;        if (fa)add(fa,i);else RT=i;    }    poi[n+1].v=-0x7ffffff,poi[n+1].po = 0;    poi[s=n+2].v=0x7ffffff,poi[s].po = 0;    std::sort(poi+1,poi+s+1,cmp);    root[0]=build(1,s);    for (int i=1;i<=s;i++)wz[poi[i].po]=i;    dfs(RT);    for (int i=1;i<=2*n;i++)        root[i]=update(root[i-1],wz[az[i]],poi[wz[az[i]]].v);    for (int i=1;i<=n;i++){        LL cl=query(root[L[i]-1],root[R[i]],m*2);        ans=std::max(ans,(cl/2)*ld[i]);    }    printf("%lld",ans);    return 0;} 
0 0