CF702F T-Shirts (排序+平衡树)

题意:
n n n种 T 恤,每种有价格 c i c_i ci 和品质 q i q_i qi
m m m 个人要买 T 恤,第 i i i 个人有 v i v_i vi​ 元,每人每次都会买一件能买得起的 q i q_i qi​ 最大的 T 恤。一个人只能买一种 T 恤一件,所有人之间都是独立的。
问最后每个人买了多少件 T 恤?如果有多个 q i q_i qi​ 最大的 T 恤,会从价格低的开始买。

题解:
对所有衣服按照 q [ i ] q[i] q[i]从大到小排序
对每个人维护 a [ i ] a[i] a[i]表示其剩余的钱, b [ i ] b[i] b[i]表示其买的衣服数量
对所有人按照 a [ i ] a[i] a[i]从小到大排序,初始 b [ i ] = 0 b[i]=0 b[i]=0
之后枚举每件衣服 j = 1 − > n j=1->n j=1>n
衣服j对人的影响就是把全局 a [ i ] > = q [ j ] a[i]>=q[j] a[i]>=q[j]的人都 a [ i ] − = q [ j ] a[i]-=q[j] a[i]=q[j],并且 b [ i ] + + b[i]++ b[i]++

用一棵平衡树维护每个人,按 a a a从小到大排列
每次找出所有 a [ i ] > = p [ j ] a[i]>=p[j] a[i]>=p[j]的人,将其 a [ i ] − = p [ j ] a[i]-=p[j] a[i]=p[j],并且 b [ i ] + + b[i]++ b[i]++
这里我们考虑令 x = p [ j ] x=p[j] x=p[j],所有在 [ 0 , x ) [0,x) [0,x)内的数不动,把所有在 [ x , 2 x ] [x,2x] [x,2x]内的数暴力修改,之后将 > 2 x >2x >2x的数直接打一个b加,a减的标记

可以发现 [ x , 2 x ] [x,2x] [x,2x]内的元素至少减半了,所以这样的暴力操作对每个点最多进行 O ( l o g v ) O(logv) O(logv)

找了一天bug,头晕加眼花
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
mt19937 rnd(233);
struct FHQ{
    int cnt,root;
    int ls[maxn],rs[maxn],val[maxn],key[maxn],siz[maxn];
    int sum[maxn],lazy[maxn],sub[maxn];

    int newnode(int x){
        val[++cnt]=x;
        key[cnt]=rnd();
        siz[cnt]=1;
        return cnt;
    }
//    void update(int node){
//        siz[node]=siz[ls[node]]+siz[rs[node]]+1;
//    }
    void push_down(int node){
        if(lazy[node]){
            if(ls[node]) lazy[ls[node]]+=lazy[node],sum[ls[node]]+=lazy[node];
            if(rs[node]) lazy[rs[node]]+=lazy[node],sum[rs[node]]+=lazy[node];
            lazy[node]=0;
        }
        if(sub[node]){
            if(ls[node]) val[ls[node]]-=sub[node],sub[ls[node]]+=sub[node];
            if(rs[node]) val[rs[node]]-=sub[node],sub[rs[node]]+=sub[node];
            sub[node]=0;
        }
    }
    void split_val(int node,int vals,int &x,int &y){
        if(!node){
            x=y=0;
            return ;
        }
        push_down(node);
        if(val[node]<=vals){
            x=node;
            split_val(rs[node],vals,rs[node],y);
        }
        else{
            y=node;
            split_val(ls[node],vals,x,ls[node]);
        }
        //update(node);
    }
    int mer(int x,int y){  //大顶堆
        if(!x||!y) return x+y;
        if(key[x]>key[y]){
            push_down(x);
            rs[x]=mer(rs[x],y);
            //update(x);
            return x;
        }
        else{
            push_down(y);
            ls[y]=mer(x,ls[y]);
            //update(y);
            return y;
        }
    }
    void ins(int vals){
        int x=0,y=0;
        split_val(root,vals,x,y);
        root=mer(mer(x,newnode(vals)),y);
    }
    int x,y,z;
    void sol(int pric){
        x=0,y=0,z=0;
        split_val(root,pric-1,x,y);
        lazy[y]++;
        sub[y]+=pric;
        sum[y]++;
        val[y]-=pric;
        split_val(y,pric-1,y,z);
        dfs(y);
        root=mer(x,z);
    }
    void dfs(int node){
        if(!node) return;
        push_down(node);
        dfs(ls[node]);
        dfs(rs[node]);
        int xx=0,yy=0;
        ls[node]=rs[node]=0;
        split_val(x,val[node],xx,yy);
        x=mer(mer(xx,node),yy);
    }
    void print(int node){
        if(!node) return ;
        push_down(node);
        print(ls[node]);
        print(rs[node]);
    }
}fhq;

struct Case{
    int c,q;
    bool operator < (const Case & a)const{
    if(q!=a.q) return q>a.q;
    else return c<a.c;
    }
}a[maxn];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].c>>a[i].q;
    }
    sort(a+1,a+1+n);
    int q;
    cin>>q;
    for(int i=1;i<=q;i++){
        int x;
        cin>>x;
        fhq.ins(x);
    }
    for(int i=1;i<=n;i++){
        fhq.sol(a[i].c);
    }
    fhq.print(fhq.root);
    for(int i=1;i<=q;i++){
        cout<<fhq.sum[i]<<" ";
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值