2809: [Apio2012]dispatching
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1477 Solved: 713
[ Submit][ Status]
Description
在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为 Master。除了 Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者 发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递 人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。写一个程序,给定每一个忍者 i的上级 Bi,薪水Ci,领导力L i,以及支付给忍者们的薪水总预算 M,输出在预算内满足上述要求时顾客满意度的最大值。
1 ≤N ≤ 100,000忍者的个数;
1 ≤M ≤ 1,000,000,000 薪水总预算;
0 ≤B
i < i 忍者的上级的编号;
1 ≤Ci ≤ M 忍者的薪水;
1 ≤Li ≤ 1,000,000,000 忍者的领导力水平。
Input
从标准输入读入数据。
第一行包含两个整数 N和 M,其中 N表示忍者的个数,M表示薪水的总预
算。
接下来 N行描述忍者们的上级、薪水以及领导力。其中的第 i 行包含三个整
B
i , C i , L i分别表示第i个忍者的上级,薪水以及领导力。Master满足B i = 0,
并且每一个忍者的老板的编号一定小于自己的编号B
i < i。
Output
输出一个数,表示在预算内顾客的满意度的最大值。
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 ,是可以得到的用户满意度的最大值。
题意:给一棵树,每个节点有C,L两个属性,找到一个点,使得在这个点的子节点中选出一些点,这些点的C的和小于M,得到一个值(选出节点的个数*此节点的L值),问这个值得最大值是多少
思路:首先DFS一次得到每个点的深度,然后按照深度从大到小依次向父节点合并。每次合并之后就是要在这个子树中找到最多的节点使得他们的C值不超过m,用treap就可以了
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=400010;
typedef long long LL;
int root[maxn],tot;
int N;
LL M,ans;
LL L[maxn];
int head[maxn];
int num;
struct E
{
int v,next;
}edge[maxn];
struct Node
{
int ch[2];
int r;
LL v;
LL sum;
int cnt;
int s;
void init(LL val,int p){v=val;ch[0]=ch[1]=0;cnt=s=p;r=rand();}
int cmp(LL x)const
{
if(x==v)return -1;
return x<v?0:1;
}
}tree[maxn*2];
void maintain(int x)
{
tree[x].s=tree[x].cnt;
tree[x].s+=tree[tree[x].ch[0]].s+tree[tree[x].ch[1]].s;
tree[x].sum=tree[tree[x].ch[0]].sum+tree[tree[x].ch[1]].sum+(LL)tree[x].cnt*tree[x].v;
}
void rotate(int &o,int d)
{
int k=tree[o].ch[d^1];
tree[o].ch[d^1]=tree[k].ch[d];
tree[k].ch[d]=o;
maintain(o);
maintain(k);
o=k;
}
void insert(int &o,LL x,int cnt)
{
if(!o)
{
o=++tot;
tree[o].init(x,cnt);
}
else
{
if(x==tree[o].v)tree[o].cnt+=cnt;
else
{
int d=(x<tree[o].v?0:1);
insert(tree[o].ch[d],x,cnt);
if(tree[tree[o].ch[d]].r>tree[o].r)
rotate(o,d^1);
}
}
maintain(o);
}
void remove(int &o,LL x)
{
if(!o)return;
int d=tree[o].cmp(x);
if(d==-1)
{
int u=o;
if(tree[o].ch[0]&&tree[o].ch[1])
{
int d2=(tree[tree[o].ch[0]].r>tree[tree[o].ch[1]].r?1:0);
rotate(o,d2);
remove(tree[o].ch[d2],x);
}
else
{
if(!tree[o].ch[0])o=tree[o].ch[1];
else o=tree[o].ch[0];
}
}
else remove(tree[o].ch[d],x);
if(o)maintain(o);
}
void add_edge(int fa,int v)
{
edge[num].v=v;
edge[num].next=head[fa];
head[fa]=num++;
}
void merge(int &src,int &dest)
{
if(!src)return;
merge(tree[src].ch[0],dest);
merge(tree[src].ch[1],dest);
insert(dest,tree[src].v,tree[src].cnt);
remove(src,tree[src].v);
}
int query(int o,LL x)
{
if(!o)return 0;
LL tmp=tree[o].v*tree[o].cnt;
if(tree[tree[o].ch[0]].sum>=x)return query(tree[o].ch[0],x);
else if(tree[tree[o].ch[0]].sum+tmp<x)
return tree[tree[o].ch[0]].s+tree[o].cnt+query(tree[o].ch[1],x-tmp-tree[tree[o].ch[0]].sum);
return tree[tree[o].ch[0]].s+(x-tree[tree[o].ch[0]].sum)/tree[o].v;
}
void dfs(int u)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
dfs(v);
if(tree[root[u]].s<tree[root[v]].s)
swap(root[u],root[v]);
merge(root[v],root[u]);
}
ans=max(ans,L[u]*query(root[u],M));
}
int main()
{
while(scanf("%d%lld",&N,&M)!=EOF)
{
tot=num=0;
int st,u;
memset(head,-1,sizeof(head));
memset(root,0,sizeof(root));
LL c;
for(int i=1;i<=N;i++)
{
scanf("%d%lld%lld",&u,&c,&L[i]);
if(!u)st=i;
else add_edge(u,i);
insert(root[i],c,1);
}
ans=0;
dfs(st);
printf("%lld\n",ans);
}
return 0;
}
splay写法:
下面的代码是改动了上面博客的二分:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
#define N 1100000
#define inf 0x6ffffffffffffffLL
int ch[N][2],siz[N],pre[N];
ll val[N],sum[N];
int rank[N];
int e[N],ne[N*2],v[N*2];
int b[N],nn,n;
ll m;
ll L[N],c[N];
void add(int x,int y){
ne[++nn]=e[x],e[x]=nn,v[nn]=y;
}
void up(int x){
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
}
void rotate(int x){
int y=pre[x],z=pre[y],k=(ch[y][0]==x);
pre[ch[y][!k]=ch[x][k]]=y;
pre[ch[x][k]=y]=x;
pre[x]=z;
if(z)ch[z][ch[z][1]==y]=x;
up(y);
}
void splay(int x,int fa){
for(;pre[x]!=fa;){
int y=pre[x],z=pre[y];
if(z==fa)rotate(x);
else if((ch[z][1]==y)==(ch[y][1]==x))rotate(y),rotate(x);
else rotate(x),rotate(x);
}
up(x);
}
int cha(int x,int y){
int now=x,k;
while(1){
k=(val[x]<val[y]);
if(!ch[x][k]){
ch[y][0]=0,ch[y][1]=0;
sum[y]=val[y];
siz[y]=1;
ch[x][k]=y;
pre[y]=x;
splay(y,0);
break;
}else x=ch[x][k];
}
return y;
}
int qu[N],bo,he;
int e1;
void bing(int x,int y){
splay(x,0),splay(y,0);//这里的旋转是为了下面的一句话
if(siz[x]<siz[y])swap(x,y);
for(qu[bo=he=1]=y;he>=bo;bo++){
e1=qu[bo];
if(ch[e1][0])qu[++he]=ch[e1][0];
if(ch[e1][1])qu[++he]=ch[e1][1];
}
for(int i=1;i<=he;i++)x=cha(x,qu[i]);
}
int getcnt(int r,ll m)
{
if(!r)return 0;
if(sum[ch[r][0]]>=m)return getcnt(ch[r][0],m);
else if(sum[ch[r][0]]+val[r]<=m)
return siz[ch[r][0]]+1+getcnt(ch[r][1],m-sum[ch[r][0]]-val[r]);
return siz[ch[r][0]];
}
ll ans;
void dfs(int x){
int p=x;
for(int i=e[x];i;i=ne[i]){
dfs(v[i]);
bing(v[i],x);
}
splay(x,0);
int l=getcnt(x,m);
ans=max(ans,(ll)l*L[p]);
}
int main(){
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%lld%lld",&b[i],&c[i],&L[i]);
if(b[i])add(b[i],i);
}
for(int i=1;i<=n;i++){
siz[i]=1,sum[i]=val[i]=c[i];
}
dfs(1);
cout<<ans;
return 0;
}
左偏树现在还不会先学习一下