CF702F T-Shirts

题目
考虑把商品按质量排序之后一个个处理,这样能买当前商品的人就是拥有钱数大于等于当前商品价格的人。
那么我们现在需要支持的就是把所有剩余钱数\(\ge k\)的人钱数\(-k\),答案\(+1\)。
这东西看上去并不是那么好做,我们有一个比较优雅的结合暴力的平衡树做法。
加入当前物品的钱数是\(c_i\)。
我们把所有人按钱数分为三类:\([0,c_i),[c_i,2c_i),[2c_i,+\infty)\)。
然后我们会发现,第一类人啥也买不起,所以他们答案和钱数都不变。
第二类人答案会\(+1\),买了一件之后钱数也到了\([0,c_i)\)的范围。
而第三类人答案也会\(+1\),并且买了一件之后钱数到了\([c_i,+\infty)\)的范围。
也就是说,第三类人买完之后钱数一定比第一类、第二类人多。
这启发我们用fhq Treap,以钱数为关键字维护hfq Treap,同时记录每个人的答案。
对于没件商品,我们按照钱数把fhq Treap分裂成三个。
对于第三类人,我们可以直接打标记。
对于第一类人,我们啥也不用做。
对于第二类人,我们暴力地修改他们每个人的钱数和答案,并暴力地跟第一类人合并。
最后把第一类和第三类merge回来。
因为每次暴力合并剩余钱数至少会减半,所以一个人最多被暴力合并\(\log b\)次。
因此总的复杂度为\(O(n\log n\log b)\)。

#include<bits/stdc++.h>
#define lc ch[p][0]
#define rc ch[p][1]
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[11],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get(),f=0;while(!isdigit(c)&&c^'-')c=Get();if(c=='-')f=1,c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return f? -x:x;}
    void write(int x){int top=0;if(!x)Put('0');if(x<0)Put('-'),x=-x;while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put(' ');}
}
using namespace IO;
void swap(int &x,int &y){x^=y^=x^=y;}
const int N=200007;
struct node{int c,q;}T[N];queue<int>q;
int n,m,ch[N][2],laz[N],tag[N],sum[N],val[N],dat[N],root,tot,x,y,z;
int New(int x){return val[++tot]=x,dat[tot]=rand(),tot;}
void pushdown(int p)
{
    if(laz[p])
    {
    if(lc) sum[lc]+=laz[p],laz[lc]+=laz[p];
    if(rc) sum[rc]+=laz[p],laz[rc]+=laz[p];
    laz[p]=0;
    }
    if(tag[p])
    {
    if(lc) val[lc]-=tag[p],tag[lc]+=tag[p];
    if(rc) val[rc]-=tag[p],tag[rc]+=tag[p];
    tag[p]=0;
    }
}
void split(int p,int k,int &x,int &y)
{
    if(!p) return (void)(x=y=0);
    pushdown(p),val[p]<=k? (x=p,split(rc,k,rc,y)):(y=p,split(lc,k,x,lc));
}
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    return dat[x]<dat[y]? (pushdown(x),ch[x][1]=merge(ch[x][1],y),x):(pushdown(y),ch[y][0]=merge(x,ch[y][0]),y);
}
void update(int p)
{
    pushdown(p);
    if(lc) update(lc);
    if(rc) update(rc);
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i) T[i].c=read(),T[i].q=read();
    sort(T+1,T+n+1,[](node a,node b){return a.q>b.q||(a.q==b.q&&a.c<b.c);}),m=read();
    for(int i=1,v;i<=m;++i) v=read(),split(root,v,x,y),root=merge(merge(x,New(v)),y);
    for(int i=1,p,a,b;i<=n;++i)
    {
    while(!q.empty()) q.pop();
    split(root,T[i].c-1,x,y),split(y,T[i].c*2-1,y,z);
    ++sum[z],++laz[z],val[z]-=T[i].c,tag[z]+=T[i].c;
    if(y) q.push(y);
    for(;!q.empty();)
    {
        p=q.front(),q.pop(),pushdown(p);
        if(lc) q.push(lc);
        if(rc) q.push(rc);
        lc=rc=laz[p]=tag[p]=0,++sum[p],val[p]-=T[i].c;
        split(x,val[p],a,b),x=merge(merge(a,p),b);
    }
    root=merge(x,z);
    }
    update(root);
    for(int i=1;i<=m;++i) write(sum[i]);
    return Flush(),0;
}
上一篇:LeetCode: 523. 连续的子数组和


下一篇:利用awt网格包布局管理器做课程表