[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]排队 树状数组套平衡树的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 2141 排队(块套树,分块,树状数组)BZOJ修复工程