[BZOJ 2141][国家集训队 2011]排队 树状数组套平衡树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ 2141][国家集训队 2011]排队 树状数组套平衡树相关的知识,希望对你有一定的参考价值。

这道题也就是一个动态逆序对嘛,本质上就是个查询区间排名

刚刚打了一道 [CQOI 2011]动态逆序对  用的线段树套平衡树,代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 101000
#include<cstdlib>
#define LL long long
#define lc(x) (x->ch[0])
#define rc(x) (x->ch[1])
#define size(x) ((x)?(x->size):0)
struct Treap{
    Treap *ch[2];
    int key,v;LL size;
    Treap(int x=0){
        v=x;ch[0]=ch[1]=NULL;size=1;key=rand();
    }
}*root[N*4];
void turn(Treap *&rt,int d){
    Treap *t=rt->ch[d^1];
    rt->ch[d^1]=t->ch[d]; rt->size=size(lc(rt))+size(rc(rt))+1;
    t->ch[d]=rt; t->size=size(lc(t))+size(rc(t))+1;
    rt=t;
}
void insert(Treap *&rt,int x){
    if(rt==NULL){
        rt=new Treap(x);return;
    }
    int d=rt->v < x;
    insert(rt->ch[d],x);
    if(rt->ch[d]->key < rt->key) turn(rt,d^1);
    if(rt) rt->size=size(lc(rt))+size(rc(rt))+1;
}
void del(Treap *&rt,int x){
    if(rt->v == x){
        if(lc(rt)&&rc(rt)){
            int d=lc(rt)->key < rc(rt)->key;
            turn(rt,d);del(rt->ch[d],x);
        }
        else{
            Treap *t=NULL;
            if(lc(rt)) t=lc(rt);
            else t=rc(rt);
            delete rt;rt=t;
        }
    }
    else{
        int d=rt->v < x;
        del(rt->ch[d],x);
    }
    if(rt) rt->size=size(lc(rt))+size(rc(rt))+1;
}
int a[N],pos[N];
int n,m;
void build(int l,int r,int rt){
    pos(i,l,r) insert(root[rt],a[i]);
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
LL pai(Treap *&rt,int x){
    if(!rt) return 0;
    if(rt->v >= x) return pai(lc(rt),x);
    else return size(lc(rt))+1+pai(rc(rt),x);
}
LL query(int flag,int xl,int xr,int l,int r,int num,int rt){
    if(l>=xl&&r<=xr){
        if(flag) return pai(root[rt],num);
        else return size(root[rt])-pai(root[rt],num);
    } 
    int mid=(l+r)>>1;
    int res(0);
    if(xl<=mid) res+=query(flag,xl,xr,l,mid,num,rt<<1);
    if(xr>mid) res+=query(flag,xl,xr,mid+1,r,num,rt<<1|1);
    return res;
}
void Del(int pos,int x,int l,int r,int rt){
    del(root[rt],x);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) Del(pos,x,l,mid,rt<<1);
    else Del(pos,x,mid+1,r,rt<<1|1);
}
LL ans;
inline int read(){
    int sum(0);char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘){
        sum=sum*10+ch-‘0‘;
        ch=getchar();
    }
    return sum;
}
int lowbit(int x){
    return x&(-x);
}
int c[N];
void add(int x,int num){
    while(x<=n){
        c[x]+=num;x+=lowbit(x);
    }
}
LL tot(int x){
    LL res(0);
    while(x>0){
        res+=c[x];x-=lowbit(x);
    }
    return res;
}
int main(){
    n=read();m=read();
    pos(i,1,n){
        a[i]=read();
        pos[a[i]]=i;
    }
    build(1,n,1);
    pos(i,1,n){
        ans+=i-1-tot(a[i]-1);
        add(a[i],1);
    }
    pos(i,1,m){
        printf("%lld\n",ans);
        int x;x=read();
        if(pos[x]+1<=n) ans-=query(1,pos[x]+1,n,1,n,x,1);
        if(pos[x]-1>=1) ans-=query(0,1,pos[x]-1,1,n,x,1);
        Del(pos[x],x,1,n,1);
    }
    return 0;
}  

但是常数过于巨大,导致COGS上卡半天常都没过去最后一个点,只是在内网A掉了(赞一发内网评测机)


对于这道题,选择了原来没打过的树状数组套平衡树(其实本质上都是一样的啦)

假如我们交换a和b的位置(a<b),那么对答案产生影响的就是[a+1,b-1]这一段区间了

ans=ans-(区间内>b的个数)+(区间内<b的个数)-(区间内<a的个数)+(区间内>a的个数)

然后还要考虑a和b本身的大小造成的贡献

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 20100
#define lc(x) (x->ch[0])
#define rc(x) (x->ch[1])
#define size(x) ((x)?(x->size):0)
int n,m;
int a[N];
struct Treap{
    Treap *ch[2];
    int key,v,size;
    Treap(int x=0){ch[0]=ch[1]=NULL;key=rand();size=1;v=x;}
}*root[N];
void pushup(Treap *&rt){
    rt->size=size(lc(rt))+size(rc(rt))+1;
}
void turn(Treap *&rt,int d){
    Treap *t=rt->ch[d^1];
    rt->ch[d^1]=t->ch[d];pushup(rt);
    t->ch[d]=rt;pushup(t);
    rt=t;
}
void insert(Treap *&rt,int x){
    if(!rt){
        rt=new Treap(x);
        return;
    }
    int d=rt->v < x;
    insert(rt->ch[d],x);
    if(rt->ch[d]->key < rt->key) turn(rt,d^1);
    if(rt) pushup(rt);
}
void del(Treap *&rt,int x){
    if(rt->v==x){
        if(lc(rt)&&rc(rt)){
            int d=lc(rt)->key < rc(rt)->key;
            turn(rt,d);del(rt->ch[d],x);
        }
        else{
            Treap *t;
            if(lc(rt)) t=lc(rt);
            else t=rc(rt);
            delete rt;rt=t;
        }
    }
    else{
        int d=rt->v < x;
        del(rt->ch[d],x);
    }
    if(rt) pushup(rt);
}
int lowbit(int x){
    return x&(-x);
}
int ans;
void add(int x,int num,int flag){
    while(x<=n){
        if(flag) insert(root[x],num);
        else del(root[x],num);
        x+=lowbit(x);
    }
}
int pai_min(Treap *&rt,int num){
    if(!rt) return 0;
    if(rt->v >= num) return pai_min(lc(rt),num);
    else return size(lc(rt))+1+pai_min(rc(rt),num);
}
int pai_max(Treap *&rt,int num){
    if(!rt) return 0;
    if(rt->v > num) return size(rc(rt))+1+pai_max(lc(rt),num);
    else return pai_max(rc(rt),num);
}
int tot_max(int x,int num){
    int sum(0);
    while(x>0){
        sum+=pai_max(root[x],num);
        x-=lowbit(x);
    }
    return sum;
}
int tot_min(int x,int num){
    int sum(0);
    while(x>0){
        sum+=pai_min(root[x],num);
        x-=lowbit(x);
    }
    return sum;
}
vector<int> b;
int findx(int x){
    return lower_bound(b.begin(),b.end(),x)-b.begin()+1;
}
int main(){
    scanf("%d",&n);
    pos(i,1,n){
        scanf("%d",&a[i]);b.push_back(a[i]);
    }
    sort(b.begin(),b.end());
    b.erase(unique(b.begin(),b.end()),b.end());
    pos(i,1,n){
        a[i]=findx(a[i]);add(i,a[i],1);
    }
    pos(i,1,n){
        ans+=tot_max(i-1,a[i]);
    }
    scanf("%d",&m);
    printf("%d\n",ans);
    pos(i,1,m){
        int x,y;scanf("%d%d",&x,&y);
        if(x>y) swap(x,y);
        ans-=tot_max(y-1,a[y])-tot_max(x,a[y]);
        ans+=tot_min(y-1,a[y])-tot_min(x,a[y]);
        ans-=tot_min(y-1,a[x])-tot_min(x,a[x]);
        ans+=tot_max(y-1,a[x])-tot_max(x,a[x]);
        if(a[x]>a[y]) ans--;
        else if(a[x]<a[y]) ans++;
        add(x,a[x],0);add(x,a[y],1);
        add(y,a[y],0);add(y,a[x],1);
        swap(a[x],a[y]);
        printf("%d\n",ans);
    }
    return 0;
}

谨以此纪念此类型树套树入门

 

以上是关于[BZOJ 2141][国家集训队 2011]排队 树状数组套平衡树的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj2141: 排队

Bzoj 2141: 排队 分块,逆序对,树状数组

BZOJ 2141 排队(块套树,分块,树状数组)BZOJ修复工程

BZOJ 2141 排队(块套树,分块,树状数组)BZOJ修复工程

bzoj 2141: 排队

[BZOJ2141]排队