当前位置: 代码迷 >> 综合 >> APIO2012 dispatching(dfs序+主席树)
  详细解决方案

APIO2012 dispatching(dfs序+主席树)

热度:87   发布时间:2023-12-13 04:48:41.0

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 200000
LL 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;
}