bzoj3289 Mato的文件管理 莫队算法 树状数组

bzoj3289 Mato的文件管理
题目大意: 求静态区间逆序对 n,q <= 50000
题解:
首先 莫队算法 传说可以解决一切区间问题的莫队算法……

莫队算法

所以有一个比较优雅的替代品。那就是先对序列分块。然后对于所有询问按照L所在块的大小排序。如果一样再按照R排序。然后按照排序后的顺序计算。为什么这样计算就可以降低复杂度呢。
一、i与i+1在同一块内,r单调递增,所以r是O(n)的。由于有n^0.5块,所以这一部分时间复杂度是n^1.5。
二、i与i+1跨越一块,r最多变化n,由于有n^0.5块,所以这一部分时间复杂度是n^1.5
三、i与i+1在同一块内时变化不超过n^0.5,跨越一块也不会超过2*n^0.5,不妨看作是n^0.5。由于有n个数,所以时间复杂度是n^1.5
于是就变成了O(n^1.5)了。
而我们知道 莫队算法的O(n^1.5)是基于O(1)得到[l-1,r]结果的
然而这道题 查逆序对 需要O(logn)时间用树状数组完成= =
所以复杂度O(n^1.5*logn)。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define lowbit(pos) pos&(-pos)
using namespace std;
const int M=50010;
int a[M],disc[M],bl[M],c[M];
int N,Q;
struct qu {int l,r,id,ans;}q[M];
bool cmp(qu x,qu y){
    if(bl[x.l]==bl[y.l]) return x.r<y.r;
    else return bl[x.l]<bl[y.l];
}
bool cmp2(qu x,qu y){
    return x.id<y.id;
}
void add(int pos,int x){
    while(pos<=N){
        c[pos]+=x;
        pos+=lowbit(pos);
    }
}
int query(int pos){
    int ret=0;
    while(pos){
        ret+=c[pos];
        pos-=lowbit(pos);
    }
    return ret;
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d",&N);int k=(int)sqrt(N);//k numbers in each block
    for(int i=1;i<=N;i++){
        scanf("%d",&a[i]),disc[i]=a[i];
        bl[i]=(i-1)/k+1;
    }
    sort(disc+1,disc+1+N);
    int size=unique(disc,disc+1+N)-disc-1;
    disc[0]=-1;
    for(int i=1;i<=N;i++) 
        a[i]=lower_bound(disc+1,disc+1+size,a[i])-disc;
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+1+Q,cmp);
    int now=0;int l=1,r=0;
    for(int i=1;i<=Q;i++){
        while(q[i].l<l) {l--;add(a[l],1);now+=query(a[l]-1);}
        while(q[i].r>r) {r++;add(a[r],1);now+=r-l+1-query(a[r]);}
        while(q[i].l>l) {add(a[l],-1);now-=query(a[l]-1);l++;}
        while(q[i].r<r) {add(a[r],-1);now-=(r-l-query(a[r]));r--;}
        q[i].ans=now;
    }
    sort(q+1,q+1+Q,cmp2);
    for(int i=1;i<=Q;i++) printf("%d\n",q[i].ans);
    return 0;
}

你可能感兴趣的:(树状数组,bzoj,莫队算法)