codeforces 702F T-shirt

22 篇文章 0 订阅
5 篇文章 0 订阅

题目大意

给出n件T-shirt的重要程度q[i]和花费c[i],有k个人最开始分别有b[i]的金钱,每个人的选衣服的策略都是一样的:将所有T-shirt按照重要程度从大到小排序,重要程度相同的按花费从小到大排,然后每个人从头开始取T-shirt,如果金钱数大于当前的T-shirt的花费,那么就买下这件衣服,问每个人最多能够买的T-shirt数量。

暴力

暴力很显然,就是一个个枚举吧。
另外一种暴力是这样的:从前开始枚举每一件T-shirt,枚举当前的所有人剩下的钱,然后减去可以买的。

可持久化Treap

根据第二种暴力,我们可以用一棵可持久化Treap来维护所有人剩下的钱数,对于当前枚举的一件T-shirt,我们可以将Treap split掉,分成大于等于c[i](设为A)和小于c[i](设为B)两部分,然后将A的减掉c[i],然后我们考虑合并两部分,由于Treap的合并要满足一棵树的点权要完全小于第二棵树的点权,于是我们要暴力把A中小于B中最大值的点加到B中,然后就merge一下。
为什么这样的时间复杂度是可行的呢?
考虑一个要加到B中的点x,假设我们原来值为b,那么减完之后就变成了b-c[i],再设B中最大值为a
bc[i]<aa<c[i]bc[i]<c[i]12b<c[i]
什么意思呢?就是每次一个数会被暴力加到B当且仅当从c[i]是大于它的 12 的,所以每个数最多会被暴力加到B中 log2n 次,所以总的时间复杂度是 O(n(log2n)2) 的。

第一次打可持久化Treap
哈哈哈跑得比富榄快
附图:
这里写图片描述
这里写图片描述

贴个标程(PS:由于我的del删的都是最小的点所以写成了另外一种样子):

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second

using namespace std;

typedef long long LL;
typedef double db;

int get(){
    char ch;
    int s=0;
    while(ch=getchar(),ch<'0'||ch>'9');
    s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    return s;
}

typedef pair<int,int> P;

const int N = 2e+5+10;

struct T_shirts{
    int c,q;
}a[N];
struct point{
    int l,r,size,key,v,ad,num,ans,d;//ad for v;d for ans
}tree[N];
int n,k,root,tot;
int ans[N];

bool cmp(T_shirts a,T_shirts b){
    if (a.q!=b.q)return a.q>b.q;
    return  a.c<b.c;
}

void down(int now){
    int l=tree[now].l,r=tree[now].r;
    if (tree[now].ad){
        if (l){tree[l].ad+=tree[now].ad;tree[l].v-=tree[now].ad;}
        if (r){tree[r].ad+=tree[now].ad;tree[r].v-=tree[now].ad;}
        tree[now].ad=0;
    }
    if (tree[now].d){
        if (l){tree[l].ans+=tree[now].d;tree[l].d+=tree[now].d;}
        if (r){tree[r].ans+=tree[now].d;tree[r].d+=tree[now].d;}
        tree[now].d=0;
    }
}

void change(int now){
    tree[now].size=tree[tree[now].l].size+tree[tree[now].r].size+1;
}

int merge(int x,int y){
    if (!x||!y)return x^y;
    down(x);down(y);
    if (tree[x].key<tree[y].key){
        tree[x].r=merge(tree[x].r,y);
        change(x);
        return x;
    }
    else{
        tree[y].l=merge(x,tree[y].l);
        change(y);
        return y;
    }
}

P split(int now,int tot){
    if (!tot)return P(0,now);
    down(now);
    if (tree[tree[now].l].size>=tot){
        P f=split(tree[now].l,tot);
        tree[now].l=f.se;
        change(now);
        return P(f.fi,now);
    }
    P f=split(tree[now].r,tot-tree[tree[now].l].size-1);
    tree[now].r=f.fi;
    change(now);
    return P(now,f.se);
}

int gettot(int now,int v){
    down(now);
    if (!now)return 0;
    if (tree[now].v>=v)return gettot(tree[now].l,v);
    return gettot(tree[now].r,v)+tree[tree[now].l].size+1;
}

void insert(int &now,int v,int ans,int num,int w){
    int s=gettot(now,v);
    P f=split(now,s);
    int u=f.fi,e=f.se;
    tree[w].v=v;
    tree[w].ans=ans;
    tree[w].num=num;
    tree[w].key=rand();
    tree[w].size=1;
    u=merge(u,w);
    now=merge(u,e);
}

void del(int &now,int x){
    P f=split(now,1);
    now=f.se;
}

int kth(int now,int k){
    down(now);
    if (tree[tree[now].l].size+1==k)return now;
    if (k<=tree[tree[now].l].size)return kth(tree[now].l,k);
    return kth(tree[now].r,k-tree[tree[now].l].size-1);
}

void init(){
    n=get();
    srand(116786);
    fo(i,1,n){a[i].c=get();a[i].q=get();}
    sort(a+1,a+1+n,cmp);
    k=get();
    fo(i,1,k){
        if(i==1){
            tot=root=tree[1].size=tree[1].num=1;
            tree[1].v=get();
            tree[1].key=rand();
        }
        else{
            int x=get();
            insert(root,x,0,i,i);
        }
    }
}

void work(){
    fo(i,1,n){
        int s=gettot(root,a[i].c);
        P f=split(root,s);
        int u=f.fi,v=f.se;
        if (!v){root=u;continue;}
        tree[v].d++;
        tree[v].ans++;
        tree[v].ad+=a[i].c;
        tree[v].v-=a[i].c;
        if (!u){root=v;continue;}
        int mv=tree[kth(u,tree[u].size)].v;
        for(int x=kth(v,1);tree[x].v<mv&&v;x=kth(v,1)){
            del(v,x);
            insert(u,tree[x].v,tree[x].ans,tree[x].num,tree[x].num);
            if (!v)break;
        }
        if (!v){root=u;continue;}
        root=merge(u,v);
    }
}

void getans(int now){
    down(now);
    if (tree[now].l)getans(tree[now].l);
    if (tree[now].r)getans(tree[now].r);
    ans[tree[now].num]=tree[now].ans;
}

void putans(){
    getans(root);
    fo(i,1,k)printf("%d ",ans[i]);putchar('\n');
}

int main(){
    init();
    work();
    putans();
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值