【bzoj 3744】Gty的妹子序列(分块+树状数组)

传送门biu~
默认按 size=n s i z e = n 分块(求导个屁)。对于每个询问,有三个部分:
①对于大块之间形成的逆序对,
因为求长度为n的序列的逆序对数可以用树状数组在 O(nlogn) O ( n log ⁡ n ) 的时间复杂度内完成,所以可以在 O(nnlogn) O ( n n log ⁡ n ) 的复杂度内预处理 ani,j a n i , j 表示块 i i ~j的逆序对数。在 O(1) O ( 1 ) 的时间复杂度内询问。
②对于零散的部分和大块形成的逆序对,
O(nn) O ( n n ) 的复杂度内预处理 Loweri,j L o w e r i , j 代表第 i i ~第n块中小于j的数有多少个, Upperi,j U p p e r i , j 代表第 1 1 ~i块中小于j的数有多少个。因为零散部分的熟练不超过 n n ,所以可以在 O(n) O ( n ) 的时间复杂度内询问。
③对于零散的部分之间形成的逆序对,
用树状数组直接计算逆序对数,因为零散部分的熟练不超过 n n ,所以可以在 O(nlogn) O ( n log ⁡ n ) 的时间复杂度内计算。
综上,时间复杂度最大为 O(Tnlogn) O ( T n log ⁡ n )

#include<bits/stdc++.h>
#define N 50005
using namespace std;
int n,m,ans,a[N],b[N],BIT[N],t[N],tim,block[N],blocksize;
int an[233][233],Lower[233][N],Upper[233][N];
inline void add(int x){
    for(int i=x;i<=m;i+=i&-i){
        if(tim^t[i])    t[i]=tim,BIT[i]=0;
        ++BIT[i];
    }
}
inline int search(int x){
    int res(0);
    for(int i=x;i;i-=i&-i){
        if(tim^t[i])    t[i]=tim,BIT[i]=0;
        res+=BIT[i];
    }   
    return res;
}
inline void init(){
    scanf("%d",&n);blocksize=sqrt(n);
    for(int i=1;i<=n;++i)   scanf("%d",&a[i]),b[i]=a[i],block[i]=(i-1)/blocksize+1;
    sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i){
        a[i]=lower_bound(b+1,b+m+1,a[i])-b;
        ++Lower[block[i]][a[i]+1],++Upper[block[i]][a[i]-1];
    }
    for(int i=1;i<=block[n];++i){
        for(int j=1;j<=m;++j)   Lower[i][j]+=Lower[i][j-1];
        for(int j=m;j>=1;--j)   Upper[i][j]+=Upper[i][j+1];     
    }
    for(int j=1;j<=m;++j){
        for(int i=block[n];i>=1;--i)    Lower[i][j]+=Lower[i+1][j];
        for(int i=1;i<=block[n];++i)    Upper[i][j]+=Upper[i-1][j];
    }
    for(int i=1;i<=block[n];++i){
        ++tim;
        for(int now=0,j=(i-1)*blocksize+1;j<=n;++j){
            now+=search(m)-search(a[j]);
            if(block[j]^block[j+1]) an[i][block[j]]=now;
            add(a[j]);
        }
    }
}
inline void solve(){
    int T,l,r;
    scanf("%d",&T);
    while(T--){
        ++tim;
        scanf("%d%d",&l,&r);
        l^=ans,r^=ans,ans=0;
        if(block[r]-block[l]<=1){
            for(int i=l;i<=r;++i)
                ans+=search(m)-search(a[i]),add(a[i]);
        }
        else{
            ans=an[block[l]+1][block[r]-1];
            for(int i=l;block[i]==block[l];++i){
                ans+=Lower[block[i]+1][a[i]]-Lower[block[r]][a[i]];
                ans+=search(m)-search(a[i]),add(a[i]);
            }
            for(int i=(block[r]-1)*blocksize+1;i<=r;++i){
                ans+=Upper[block[i]-1][a[i]]-Upper[block[l]][a[i]];
                ans+=search(m)-search(a[i]),add(a[i]);
            }
        }
        printf("%d\n",ans);
    }
}
int main(){
    init();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zP1nG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值