【数据结构】【范浩强Treap】CF702F T-shirt

本文介绍了一种使用Treap(随机化平衡二叉搜索树)解决特定购物问题的方法。问题涉及顾客购买T恤的策略,顾客会在自己的预算范围内选择价值最高的T恤购买,且每款T恤只能购买一次。文章详细解释了如何构建Treap来实现区间加减操作,并通过实例代码展示了具体的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

有n件T-shirt,每件T-shirt有一个val值和price值
还有m个顾客,每个顾客有一个最大金额Bi
每个顾客的购买策略是相同的:
他会买他的资金范围内val值最大的一件,
而且每种T-shirt都只能买1次
直到所有的T-shirt他都买不起或者他都买过了,那么

n,m<=100000

分析

其实这道题的难点主要在第一步,就是如何建树,
很容易就会想到用T-shirt来建树,但是这样的话其实是很难做的(反正我没想出来)
因为你既要考虑不能重复买,又不能很容易地做到满足时间复杂度

换一种思路,用人的钱来建平衡树
这样一来就很容易了,因为每个人买衣服的策略是一样的
所以如果按照T-shirt的优先级排序,再依次考虑,就会显得很简单

假设当前考虑到第i件T-shirt,其price值为Pi
那么很容易想到,在平衡树中所有权值大于Pi的点,
都要减去Pi,这样一来,我们就要考虑平衡树区间加减就可以了

那么如何用神奇的范浩强Treap来处理平衡树的区间加减呢?
画个图分析一下(肥肠抱歉,下面两个图Pi写成了Vi):
这里写图片描述
这里写图片描述
很容易发现,平衡树中减去Pi后会有值域重复部分,这样的部分不可能通过Merge操作来完成
那么要怎么做呢????

其实很简单,暴力地将其中一部分中每个节点依次插入平衡树即可。
你是不是本能地认为会超时?
然而,经过很简单的证明,可以得到是不会超时的。

Bi为第i个人的金额,Pi为他需要支付的钱
那么如果这个人需要暴力插入,那么必须满足:

PiBi2Pi

所以
Bi2Pi

要知道的是,Bi每次操作后都需要减去Pi,简而言之,如果要暴力操作,那么Bi至少要减去它的一半,所以最多会被暴力log(Bi)次,就不会超时了

我的范浩强Treap的模板比较丑,下面这个代码卡时限卡得非常紧,因此加了一堆优化,但主体思路并没变。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 200010
using namespace std;
struct node{
    int ch[2];
    int key,fix,sum,add,cnt,cnts;
    int id;
    node() {}
    node(int key1,int id1):key(key1),fix(rand()),sum(1),add(0),cnt(0),cnts(0),id(id1) {}
}Tree[MAXN];
int root,ncnt;
void update(int x){
    Tree[x].sum=Tree[Tree[x].ch[0]].sum+Tree[Tree[x].ch[1]].sum+1;
    Tree[x].cnt+=Tree[x].cnts;
    Tree[x].key+=Tree[x].add;
    if(Tree[x].ch[0]){
        Tree[Tree[x].ch[0]].add+=Tree[x].add;
        Tree[Tree[x].ch[0]].cnts+=Tree[x].cnts;
    }
    if(Tree[x].ch[1]){
        Tree[Tree[x].ch[1]].add+=Tree[x].add;
        Tree[Tree[x].ch[1]].cnts+=Tree[x].cnts;
    }
    Tree[x].cnts=0;
    Tree[x].add=0;
}
int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    update(x);
    update(y);
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
        update(y);
        return y;
    }
}
pair<int,int> Split(int x,int k){
    if(x==0) return make_pair(0,0);
    pair<int,int> y;
    update(x);
    if(Tree[x].key<=k){
        y=Split(Tree[x].ch[1],k);
        Tree[x].ch[1]=y.first;
        update(x);
        y.first=x;
    }
    else{
        y=Split(Tree[x].ch[0],k);
        Tree[x].ch[0]=y.second;
        update(x);
        y.second=x;
    }
    return y;
}
void Insert(int val,int id){
    Tree[++ncnt]=node(val,id);
    Tree[ncnt].ch[0]=0;
    Tree[ncnt].ch[1]=0;
    root=Merge(root,ncnt);
}
void dfs1(int x){
    if(x==0)
        return ;
    update(x);
    dfs1(Tree[x].ch[0]);
    dfs1(Tree[x].ch[1]);
    Tree[x].ch[0]=0;
    Tree[x].ch[1]=0;
    pair<int,int> y=Split(root,Tree[x].key);
    root=Merge(Merge(y.first,x),y.second);
}
void solve(int val){
    pair<int,int> x=Split(root,val-1);
    Tree[x.second].add-=val;
    Tree[x.second].cnts++;
    //PF("[%d %d]",x.first,x.second);
    pair<int,int> y=Split(x.second,val);
    if(Tree[x.first].sum<Tree[y.first].sum){
        root=Merge(y.first,y.second);
        dfs1(x.first);
    }
    else{
        root=Merge(x.first,y.second);
        dfs1(y.first);
    }
}
int ans[MAXN];
void dfs(int x,int sum){
    if(x==0)
        return ;
    ans[Tree[x].id]=sum+Tree[x].cnt+Tree[x].cnts;
    dfs(Tree[x].ch[0],sum+Tree[x].cnts);
    dfs(Tree[x].ch[1],sum+Tree[x].cnts);
}
int n,m,x,id;
pair<int,int> p[MAXN];
void Read(int &x){
    char c;
    bool flag=0;
    while(c=getchar(),c!=EOF&&(c<'0'||c>'9')&&c!='-')
    if(c=='-'){
        c=getchar();
        flag=1;
    }
    x=c-'0';
    while(c=getchar(),c!=EOF&&c>='0'&&c<='9')
        x=x*10+c-'0';
}
pair<int,int> cost[MAXN];
int main(){
    srand(32);
    SF("%d",&n);
    for(int i=1;i<=n;i++){
        Read(p[i].second);
        Read(p[i].first);
        p[i].first*=-1;
    }
    sort(p+1,p+1+n);
    SF("%d",&m);

    for(int i=1;i<=m;i++){
        SF("%d",&cost[i].first);
        cost[i].second=i;
    }
    sort(cost+1,cost+1+m);
    for(int i=1;i<=m;i++)
        Insert(cost[i].first,cost[i].second);
    for(int i=1;i<=n;i++){
        /*for(int j=1;j<=m;j++)
            PF("[%d %d %d %d %d]lson:%d  rson:%d\n",j,Tree[j].key,Tree[j].add,Tree[j].cnt,Tree[j].cnts,Tree[j].ch[0],Tree[j].ch[1]);
        PF("------------------------------------\n");*/
        solve(p[i].second);
    }
    dfs(root,0);
    for(int i=1;i<=m;i++)
        PF("%d ",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值