BZOJ 3744: Gty的妹子序列(分块+树状数组)

传送门

解题思路

  首先分块预处理,设\(sum[i][j]\)为第\(i\)块到第\(j\)块的逆序对数量,\(g[i][j]\)表示前\(i\)块数值\(<=j\)的数量,这两个东西是可以\(O(n\sqrt(n)log(n))\)预处理出来的。询问的时候大块直接查询\(sum\),边角元素之间的贡献直接树状数组,边角元素与大块之间的贡献变成前缀和用\(g\)数组,时间复杂度\(O(n\sqrt(n)log(n))\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int N=50005;
const int M=300;

inline int rd(){
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;  
}

int n,a[N],cpy[N],lstans,u,num,m,b[N];
int sum[M][M],siz,bl[N],l[M],r[M],g[M][N];

struct BIT{
    int f[N];
    inline void clear(){
        memset(f,0,sizeof(f));  
    }
    inline void add(int x,int k){
        for(;x<=u;x+=x&-x) f[x]+=k; 
    }
    inline int query(int x){
        int ret=0;
        for(;x;x-=x&-x) ret+=f[x];
        return ret; 
    }
}tree;

inline void prework(){
    for(int i=1;i<=n;i++) g[bl[i]][a[i]]++;
    for(int i=1;i<=num;i++) 
        for(int j=1;j<=u;j++)
            g[i][j]+=g[i][j-1];
    for(int i=2;i<=num;i++)
        for(int j=1;j<=u;j++)
            g[i][j]+=g[i-1][j];
    for(int i=1;i<=num;i++){
        tree.clear();
        for(int j=l[i];j<=n;j++){
            sum[i][bl[j]]+=tree.query(u)-tree.query(a[j]);
            tree.add(a[j],1);
        }
    }
    for(int i=1;i<=num;i++)
        for(int j=i+1;j<=num;j++)
            sum[i][j]+=sum[i][j-1];
}   

inline int query(int x,int y){
    tree.clear(); int ret=0;
    if(bl[x]==bl[y]){
        for(int i=x;i<=y;i++){
            ret+=tree.query(u)-tree.query(a[i]);
            tree.add(a[i],1);   
        }
        return ret;
    }
    ret=sum[bl[x]+1][bl[y]-1];
    for(int i=x;i<=r[bl[x]];i++){
        ret+=tree.query(u)-tree.query(a[i]);
        if(bl[y]-1>bl[x]);
        ret+=g[bl[y]-1][a[i]-1]-g[bl[x]][a[i]-1];
        tree.add(a[i],1);
    }
    for(int i=l[bl[y]];i<=y;i++){
        ret+=tree.query(u)-tree.query(a[i]);
        if(bl[y]-1>bl[x]);
        ret+=(g[bl[y]-1][u]-g[bl[x]][u])-(g[bl[y]-1][a[i]]-g[bl[x]][a[i]]);
        tree.add(a[i],1);   
    }
    return ret;
}

int main(){
    n=rd(); siz=sqrt(n)+1; num=n/siz; if(n%siz) num++;
    for(int i=1;i<=n;i++) cpy[i]=a[i]=rd(),bl[i]=(i-1)/siz+1;
    for(int i=1;i<=num;i++) l[i]=(i-1)*siz+1,r[i]=i*siz;
    sort(cpy+1,cpy+1+n); u=unique(cpy+1,cpy+1+n)-cpy-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(cpy+1,cpy+1+u,a[i])-cpy;
    for(int i=1;i<=n;i++) b[i]=a[i]; 
    r[num]=n; prework(); m=rd(); int l,r;
    while(m--){
        l=rd(),r=rd(); l^=lstans; r^=lstans;
        lstans=query(l,r); printf("%d\n",lstans);
    }
    return 0;   
}

转载于:https://www.cnblogs.com/sdfzsyq/p/10366870.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值