BZOJ 3744: Gty的妹子序列 【分块求区间逆序对】

分块 专栏收录该内容
7 篇文章 0 订阅

题目分析:

n n   l o g n n\sqrt n~logn nn  logn的分块(+树状数组预处理)+ 主席树维护区间值求散块
n n l o g n n\sqrt {nlogn} nnlogn 的分块+树状数组处理前i块小于j(及大于j)的数有多少个,相当于用分块代替了主席树。
n n n\sqrt n nn 的分块,其实可以使上面的方法最后处理散块的时候用散块之间桶排归并,散块自身预处理前缀和/后缀和。

我打的第二种(求f[i][j]的时候循环顺序错了RE到自闭。。)。
Code:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define LL long long
#define maxn 50005
#define S 250
using namespace std;
inline void read(int &a){
    char c;while(!isdigit(c=getchar()));
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int n,m,a[maxn],N,b[maxn],bel[maxn],arr[maxn];
int s1[maxn/S+3][maxn],s2[maxn/S+3][maxn],g[maxn/S+3],f[maxn/S+3][maxn/S+3];
inline void update(int i,int d){for(;i<=n;i+=i&-i) arr[i]+=d;}
inline int getsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
int solve(int x,int y){
    int ret=0;
    for(int i=y;i>=x;i--) ret+=getsum(a[i]-1),update(a[i],1);
    for(int i=y;i>=x;i--) update(a[i],-1);
    return ret;
}
int solve(int x,int y,int l,int r){
    int ret=0;
    for(int i=r;i>=l;i--) ret+=getsum(a[i]-1),update(a[i],1);
    for(int i=y;i>=x;i--) ret+=getsum(a[i]-1),update(a[i],1);
    for(int i=r;i>=l;i--) update(a[i],-1);
    for(int i=y;i>=x;i--) update(a[i],-1);
    return ret;
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i],bel[i]=(i-1)/S;
    sort(b+1,b+1+n);
    N=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+N,a[i])-b;
    for(int i=1;i<=n;i++) s1[bel[i]][a[i]+1]++,s2[bel[i]][a[i]-1]++;
    for(int i=0;i*S<n;i++){
        for(int j=1;j<=N;j++) s1[i][j]+=s1[i][j-1];
        for(int j=1;j<=N;j++) s1[i][j]+=s1[i-1][j];
        for(int j=N;j>=1;j--) s2[i][j]+=s2[i][j+1];
        for(int j=N;j>=1;j--) s2[i][j]+=s2[i-1][j];
    }
    for(int i=0;i*S<n;i++){
        for(int j=min((i+1)*S,n);j>i*S;j--) g[i]+=getsum(a[j]-1),update(a[j],1);
        for(int j=min((i+1)*S,n);j>i*S;j--) update(a[j],-1);
        f[i][i]=g[i];
    }
    for(int i=bel[n];i>=0;i--)
        for(int j=i+1;j*S<n;j++){
            f[i][j]=f[i+1][j]+g[i];
            for(int k=i*S+1;k<=(i+1)*S;k++) f[i][j]+=s1[j][a[k]]-s1[i][a[k]];
        }
    read(m);int x,y,l,r,ans=0;
    while(m--){
        read(x),read(y),x^=ans,y^=ans,l=bel[x],r=bel[y];
        if(l+1>=r) printf("%d\n",ans=solve(x,y));
        else{
            ans=f[l+1][r-1];
            for(int i=x;i<=(l+1)*S;i++) ans+=s1[r-1][a[i]]-s1[l][a[i]];
            for(int i=r*S+1;i<=y;i++) ans+=s2[r-1][a[i]]-s2[l][a[i]];
            ans+=solve(x,(l+1)*S,r*S+1,y);
            printf("%d\n",ans);
        }
    }
}
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值