Codeforces 702F T-shirt 可持久化Treap练手

Description

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

Data Constraint

1 n, k 2105 1 ci, qi 109 1 bj 109

最暴力的做法

将所有的 T -shirt按照题目要求排序,对于每一个人,从前往后扫,能买就买,不能买就跳过,最后算出答案。

可持久化Treap

最近学了可持久化 Treap ,便找了道题来练练手。
上述做法是 O (nk),显然会超时。
将所有的人按照 bi 排序,建出一棵 Treap
然后按顺序枚举每一件 T -shirt,对于当前的第 i T- shirt ,将 Treap split 成两棵 Treap ,记为 T1 T2 T1 里的 bi 全部小于 ci (即买不起这件 T -shirt的人), T2 里的 bi 全部大于等于 ci ,于是便给 T2 打上一个标记,将 T2 内的全部 bi 减掉 ci ,答案加 1
如果我们这是便简单地将两棵Treap合并为一棵,便很有可能破坏序列的有序性。
因而,在合并之前,我们还需调整 T1 T2 ,不停调整直到使得 T1 中的最大值小于等于 T2 中的最小值,随后才能合并。
对于每次调整,找出 T2 内的最小值(记为 k ),与T1内的最大值(记为 p )比较,若k< p 则暴力用O( log n )的时间将k T2 split 掉,再用 split merge 操作把 k 加入T1中。
这样做看上去好像会超时,但我们分析一下复杂度,便能发现这是 O (n log22 n )的。
证明:
k在被减掉 ci 前的金钱数为 v v= k +ci),显然有 ci > p ,减掉ci后便有 p >v- ci
于是便有 ci > p >v- ci ,整理得 ci > v2
这也就意味着每一次 v 减完ci后会被调整(从 T2 调整到 T1 ),当且仅当 v 减去了一个大于自己一半的ci,因而对于每一个 bi ,最多会被暴力调整 log n 次,调整一次是O( log n )的,因而总的时间复杂度就是O( n log22 n <script type="math/tex" id="MathJax-Element-19102">n</script>)。

Code

#include<iostream> 
#include<cstdio>
#include<cstring>
#pragma GCC optimize(3) 
#include<algorithm> 
#include<ctime>

#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--) 
#define fi first
#define se second
#define random(x) rand()%x

using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const ll N=42e4,maxn=6e9;
struct note{
    int l,r,size;
    ll rnd;
}s[N];
struct nnn{
    ll v,c;
}gift[N],pe[N];

ll down[N],mo[N],va[N],h[N],dy[N];
int n,m,j,k,l,i,o,root;
ll ans[N];
void getdown(int o)
{
    mo[s[o].l]-=down[o];
    down[s[o].l]+=down[o];
    mo[s[o].r]-=down[o];
    down[s[o].r]+=down[o];
    va[s[o].l]+=h[o];
    va[s[o].r]+=h[o];
    h[s[o].l]+=h[o];
    h[s[o].r]+=h[o];
    down[o]=h[o]=0;
}

int merge(int a,int b)
{
    if(!(a*b))return a^b;
    if(!down[a])getdown(a);
    if(!down[b])getdown(b);
    if(s[a].rnd<s[b].rnd){
        int ls=merge(s[a].r,b);
        s[a].r=ls; 
        s[a].size=s[s[a].l].size+s[ls].size+1; 
        return a;
    }else{
        int ls=merge(a,s[b].l);
        s[b].l=ls;
        s[b].size=s[ls].size+s[s[b].r].size+1;
        return b;
    }
}

P split(int o,int wz)
{
    if(!wz)return P(0,o);
    if(down[o]!=0)getdown(o);
    if(s[s[o].l].size>=wz){
        P ls=split(s[o].l,wz);
        s[o].l=ls.se;
        s[o].size=s[s[o].r].size+s[ls.se].size+1;
        return P(ls.fi,o);
    }else{
        P ls=split(s[o].r,wz-s[s[o].l].size-1);
        s[o].r=ls.fi;
        s[o].size=s[s[o].l].size+s[ls.fi].size+1;
        return P(o,ls.se);
    }
}

int search(int o,ll p)
{
    if(o==0)return 0;
    if(down[o]!=0)getdown(o);
    if(mo[o]>=p) return search(s[o].l,p);
    else return s[s[o].l].size+1+search(s[o].r,p);
}

ll read()
{
    ll o=0; char ch=' ';
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
    return o;
}

void pri(ll o)
{
    if(!o)return;
    pri(o/10); putchar('0'+o%10);
}

void write(ll o)
{
    if(o==0)putchar('0');
    pri(o);
}

bool kmp(nnn a,nnn b){
    return a.v!=b.v ? a.v>b.v : a.c<b.c;
}

bool kmp2(nnn a,nnn b){
    return a.v<b.v;
}

int main()
{
    cin>>n;
    fo(i,1,n)gift[i].c=read(),gift[i].v=read();
    sort(gift+1,gift+n+1,kmp);
    cin>>m;
    fo(i,1,m)pe[i].v=read(),pe[i].c=i;
    sort(pe+1,pe+m+1,kmp2);
    fo(i,1,m)mo[i]=pe[i].v;
    srand(time(0));
    fo(i,1,m){
        s[i].size=1;
        s[i].rnd=random(maxn);
        if(i==1)root=1;else root=merge(root,i);
    }
    ll zd=0;
    fo(i,1,n){
        int k=search(root,gift[i].c);
        if(k==m)continue;
        if(k==0){
            mo[root]-=gift[i].c;
            va[root]+=1;
            down[root]+=gift[i].c;
            h[root]+=1;
            continue;
        }
        P bl=split(root,k);
        int le=bl.fi,ri=bl.se;
        mo[ri]-=gift[i].c;
        va[ri]+=1;
        down[ri]+=gift[i].c;
        h[ri]+=1;
        if(k!=1){
            P rr=split(le,k-1);
            zd=mo[rr.se];
            le=merge(rr.fi,rr.se);
        }else zd=mo[le];
        while(s[ri].size>0){
            P oo=split(ri,1);
            int zb=oo.fi,yb=oo.se;
            if(mo[zb]>=zd){
                ri=merge(zb,yb);
                break;
            }
            int y=search(le,mo[zb]);
            P kk=split(le,y);
            if(y!=0)zb=merge(kk.fi,zb);
            if(kk.se!=0)zb=merge(zb,kk.se);
            le=zb; s[0].size=0; ri=oo.se;
        }
        root=merge(le,ri);
    }
    int y;P kk;
    fo(i,1,m){
        if(i!=m)kk=split(root,1),y=kk.fi;
        else y=root;
        ans[pe[y].c]=va[y];
        root=kk.se;
    }
    fo(i,1,m)write(ans[i]),putchar(' ');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值