zoukankan      html  css  js  c++  java
  • BZOJ

    在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。在这个帮派里,有一名忍者被称之为 Master。除了 Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者 发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递 人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。写一个程序,给定每一个忍者 i的上级 Bi,薪水Ci,领导力L i,以及支付给忍者们的薪水总预算 M,输出在预算内满足上述要求时顾客满意度的最大值。
     
    $1≤N≤100000$ 忍者的个数;
    $1≤M≤1000000000$ 薪水总预算; 
    $0≤B_i < i$ 忍者的上级的编号;
    $1≤C_i≤M$   忍者的薪水;
    $1≤L_i≤1000000000$   忍者的领导力水平。
     
    题解:
      问题等价于,每一个点最多能在子树上取多少点,花费小于M?
      显然的是,我们的策略一定是从最小的点开始取,直到超过$M$为止,这显然是贪心的过程
      然后得枚举所有点,总复杂度$O(n^2)$
      然后考虑优化,
      我们把问题"询问一个点,其子树上之和大于$M$的$x$个权值最小的点"
      转化成"询问一个区间,查询一个区间上和小于等于$M$的$x$个最小的元素"
      显然,后者是主席树可以解决的,单次操作的复杂度就从$O(n)$变成了$O(log(n))$
      然后我们用$dfs$序将树拍扁,变成序列即可解决问题,就完成了优化
      具体过程就是
      $dfs(master)$得到$dfs$序,离散化花费
      主席树保存两个值,一个是点数,一个是花费,单点更新,区间求和
      按照$dfs$序列插入权值,构造n颗树
      最后,遍历每个点,每个点等价于一个区间询问,枚举所有点就能得到最大值
      (这道题网上的主席树题解代码真是乱写,反正我是没懂他们是怎么写的,AC是AC了,莫名其妙多此一举的操作一大堆)
    #include <bits/stdc++.h>
    #define nd seg[now]
    #define ndp seg[pre]
    #define mid ((s+t)>>1)
    #define ll long long
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,k;
    ll m;
    struct node2{
    	int to,next;
    }e[maxn];
    int	head[maxn],nume;
    int tin[maxn],tout[maxn];
    ll cost[maxn],pos[maxn],power[maxn];
    int rt[maxn],size,cnt,pre[maxn],dfn[maxn];
    int cmp(int a,int b){
    	return cost[a]<cost[b];
    }
    inline void add(int a,int b){
    	e[++nume]=(node2){b,head[a]};
    	head[a]=nume;
    }
    void dfs(int now){
    	tin[now]=++cnt;
    	dfn[cnt]=now;
    	for(int i=head[now];i;i=e[i].next){
    		dfs(e[i].to);
    	}
    	tout[now]=cnt;
    }
    struct node{
    	int l,r;ll sum,cnt;
    }seg[maxn*20];
    void maketree(int s=1,int t=n,int &now=rt[0]){
    	now=++size;nd=(node){s,t,0,0};
    	if(s==t) return ;
    	maketree(s,mid,nd.l);maketree(mid+1,t,nd.r);
    }
    void update(int &now,int pre,int k,ll cost,int s=1,int t=n){
    	now=++size;nd=ndp,nd.sum+=cost,nd.cnt++;
    	if(s==t) return ;
    	if(k<=mid)update(nd.l,ndp.l,k,cost,s,mid);
    	else update(nd.r,ndp.r,k,cost,mid+1,t);
    }
    ll query(int ndl,int ndr,ll k,int s=1,int t=n){
    	if(seg[ndr].sum-seg[ndl].sum<=k) return seg[ndr].cnt-seg[ndl].cnt;
    	if(s==t) return min(seg[ndr].cnt-seg[ndl].cnt,k/pos[s]);
    	ll sum=seg[seg[ndr].l].sum-seg[seg[ndl].l].sum;
    	if(k>=sum) return query(seg[ndl].r,seg[ndr].r,k-sum,mid+1,t)+seg[seg[ndr].l].cnt-seg[seg[ndl].l].cnt;
    	else return query(seg[ndl].l,seg[ndr].l,k,s,mid);
    }
    #undef mid
    int main(){
    	scanf("%d%lld",&k,&m);
    	int master;
    	for(int i=1;i<=k;i++){
    		scanf("%d%lld%lld",pre+i,cost+i,power+i);
    		if(pre[i]==0)master=i;
    		else add(pre[i],i);
    		pos[i]=cost[i];
    	}
    	sort(pos+1,pos+1+k);
    	n=unique(pos+1,pos+1+k)-(pos+1);
    	dfs(master);
    	maketree();
    	for(int i=1;i<=k;i++){
    		int id=lower_bound(pos+1,pos+1+n,cost[dfn[i]])-pos;
    		update(rt[i],rt[i-1],id,cost[dfn[i]]);
    	}
    	ll ans=0;
    	for(int i=1;i<=k;i++){
    		ans=max(ans,power[i]*query(rt[tin[i]-1],rt[tout[i]],m));
    	}
    	printf("%lld
    ",ans);
      return 0;
    }
    

      

     

     
      
  • 相关阅读:
    纯JS制作选项卡--JavaScript实例集锦(初学)
    超级简单实用的CSS3动画,增添网页效果
    3种方法实现图片瀑布流的效果(纯JS,Jquery,CSS)
    CSS动画小结
    遗忘比死亡更可怕
    matplotlib笔记2
    matplotlib笔记1
    pandas数据结构之Panel笔记
    pandas数据结构之Series笔记
    ndarray笔记续
  • 原文地址:https://www.cnblogs.com/nervendnig/p/9205461.html
Copyright © 2011-2022 走看看