BZOJ 2809(dfs序+离散化+主席树 或者 可并堆)

20 篇文章 0 订阅
3 篇文章 0 订阅

2809: [Apio2012]dispatching

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 5238  Solved: 2697
[Submit][Status][Discuss]

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

从标准输入读入数据。

 

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

 

接下来 N行描述忍者们的上级、薪水以及领导力。其中的第 i 行包含三个整 Bi , C i , L i分别表示第i个忍者的上级,薪水以及领导力。Master满足B i = 0并且每一个忍者的老板的编号一定小于自己的编号 Bi < 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      ,是可以得到的用户满意度的最大值。

 

Source

 

[Submit][Status][Discuss]

思路: 其实我们要求的就是一个子树,使得它的领导值* 子树中能够选择的节点数最大。

我们要枚举每一个节点,然后求他的子树中满足条件的最大节点数。

首先,我们dfs一下,求得每个点的管辖区间,并且得到一个dfs序。

我们按dfs序建立 主席树,因为主席树是权值线段树,所以我们要对每个节点的工资做个离散化,(最好一样的权值也离散化成不同的值,比较好处理) 建立主席树,并且维护孩子个数和花费的money。

然后枚举每一个节点找到他管辖的区间,r-l 根据主席树的前缀和特点一减,得到一颗线段树,然后就是一个区间查询的问题,左区间权值大于m,就进入左孩子,左孩子权值小于m就进入右孩子。然后就没有了。

代码:(代码写的跟si一样,不推荐看)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N =1e5+5;

vector<int >ve[N];
int b[N];
ll c[N],ling[N];
int n;
ll m;
int dfns[N*2];
int dfn;
int L[N],R[N];
int clo;
int vis[N];
int T[N];
int cnode;

struct yjt
{
    ll ww;
    int inde;
}yyjjtt[N];
int mp[N];

struct node
{
    int l,r;
    int ls,rs;
    int sz;
    ll sum;
}tr[3000005];

int build(int l,int r)
{
    int cur=++cnode;
    tr[cur].l=l; tr[cur].r=r; tr[cur].sum=0; tr[cur].sz=0;
    if(l==r) return cur;
    int mid=(l+r)>>1;
    tr[cur].ls=build(l,mid);
    tr[cur].rs=build(mid+1,r);
    return cur;
}

int update(int root,int pos,ll ww)
{
    int cur=++cnode;
    tr[cur]=tr[root];
    tr[cur].sz+=1; tr[cur].sum+=ww;
    if(tr[cur].l==tr[cur].r) return cur;
    int mid=(tr[cur].l+tr[cur].r)>>1;
    if(pos<=mid){
        tr[cur].ls=update(tr[root].ls,pos,ww);
    }
    else tr[cur].rs=update(tr[root].rs,pos,ww);
    return cur;
}

void dfs(int u)
{
    L[u]=++clo;
    dfns[++dfn]=u;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i];
        dfs(v);
        dfns[++dfn]=v;
    }
    R[u]=clo;
}

int query(int lt,int rt,ll res)
{
    if(tr[lt].l==tr[lt].r){
        int cc=tr[rt].sz-tr[lt].sz;
        ll now=tr[rt].sum-tr[lt].sum;
        if(now<=res) return cc;
        else return 0;
    }
    ll lsum=tr[tr[rt].ls].sum-tr[tr[lt].ls].sum;
    int lc=tr[tr[rt].ls].sz-tr[tr[lt].ls].sz;
    if(lsum>res){
        return query(tr[lt].ls,tr[rt].ls,res);
    }
    else{
        res-=lsum;
        return lc+query(tr[lt].rs,tr[rt].rs,res);
    }
}

bool cmp(yjt aa,yjt bb)
{
    if(aa.ww==bb.ww) return aa.inde<bb.inde;
    return aa.ww<bb.ww;
}

int main()
{
    scanf("%d",&n);
    scanf("%lld",&m);
    int fa;
    for(int i=1;i<=n;i++){
        scanf("%d %lld %lld",&fa,&c[i],&ling[i]);
        yyjjtt[i].ww=c[i];
        yyjjtt[i].inde=i;
        ve[fa].push_back(i);
    }

    dfs(0);
    for(int i=1;i<=n;i++) L[i]--,R[i]--;

    sort(yyjjtt+1,yyjjtt+1+n,cmp);
    for(int i=1;i<=n;i++){
        mp[yyjjtt[i].inde]=i;
    }

    T[0]=build(1,n+2);
    for(int i=2;i<dfn;i++){
        int id=dfns[i];
        //cout<<" *** id "<<id<<" ll "<<L[id]<<" "<<R[id]<<endl;
        if(vis[id]==0){
            ll ww=c[id];
            int in=mp[id];
            T[L[id]]=update(T[L[id]-1],in,ww);
            vis[id]=1;
        }
    }

    ll ans=0;
    ll tmp;
    for(int i=1;i<=n;i++){
        ll ld=ling[i];
        int l,r;
        l=L[i]; r=R[i];
        tmp=m;
        //cout<<"***** sz "<<tr[T[l-1]].sz<<" **** "<<tr[T[r]].sz<<endl;
        int cc=query(T[l-1],T[r],tmp);
        //cout<<"lingdaozhi  "<<ld <<" cc "<<cc<<endl;
        ans=max(ans,1ll*ld*cc);
    }
    printf("%lld\n",ans);

    return 0;
}

当然这个题还有一个更简便的做法就是用可并堆来实现在子树上查找小于等于m 的尽量大的节点个数,也就是我们在dfs的时候对于每个节点,递归他的左右孩子,每个孩子建立一个可并堆(大根堆),然后每次如果val>m 就pop, 然后就检查一下就可以了。

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N =1e5+5;

int n;
ll m;
ll L[N],C[N];
ll sum[N],sz[N];
vector<int >ve[N];
int root[N],cnode;
ll ans;

struct Ltree{
	int v[100005];
	int l[100005],r[100005];
	int dis[100005];
	int merge(int x,int y){
		if(x==0||y==0)return x+y;
		if(v[x]<v[y])swap(x,y);
		r[x]=merge(r[x],y);
		if(dis[r[x]]<dis[l[x]]) swap(l[x],r[x]);
		dis[x]=dis[r[x]]+1;
		//swap(l[x],r[x]);
		return x;
	}
	void pop(int &x){
		x=merge(l[x],r[x]);
	}
	int top(int x){
		return v[x];
	}
}heap;

void dfs(int u)
{
    root[u]=++cnode; heap.v[root[u]]=C[u];
    sum[u]=C[u];
    sz[u]=1;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i];
        dfs(v);
        sum[u]+=sum[v];
        sz[u]+=sz[v];
        root[u]=heap.merge(root[u],root[v]);
    }
    while(sum[u]>m){
        sz[u]--;
        sum[u]-=heap.top(root[u]);
        heap.pop(root[u]);
    }
    ans=max(ans,sz[u]*L[u]);
}

int main()
{
    int fa;
    scanf("%d%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d %lld %lld",&fa,&C[i],&L[i]);
        ve[fa].push_back(i);
    }

    dfs(1);
    printf("%lld\n",ans);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值