您的位置:首页 > 其它

treap启发式合并(BZOJ2809)||splay||左偏树

2015-03-06 21:59 363 查看

2809: [Apio2012]dispatching

Time Limit: 10 Sec Memory Limit:
128 MB

Submit: 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 ≤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 ,是可以得到的用户满意度的最大值。

题意:给一棵树,每个节点有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写法:

BZOJ2809 splay

下面的代码是改动了上面博客的二分:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
#define N 1100000
#define inf 0x6ffffffffffffffLL
int ch
[2],siz
,pre
;
ll val
,sum
;
int rank
;
int e
,ne[N*2],v[N*2];
int b
,nn,n;
ll m;
ll L
,c
;
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
,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;
}


左偏树现在还不会先学习一下
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: