n根号n解决在线无修区间逆序对问题

杂文 同时被 2 个专栏收录
83 篇文章 0 订阅
49 篇文章 0 订阅

做法

带log的分块相信大家都会,今天我们来讲讲不带log。
首先先离散化,这样值域就变成n了。
我们以根号为阈值分块,然后我们记A->B的含义是将A和B两个序列拼接在一起有多少逆序对两个元素一个来自A另一个来自B。
首先考虑如何求A->B,可以发现A和B的内部顺序是没有关系的,因此如果我们把A和B排好序了,运用归并即可线性求得A->B。
现在我们先把每个块排序,然后预处理一个位置到块头的逆序对个数(树状数组解决),接下来对于块内询问[l,r]假设块头和块尾分别是u和v,那么。
ans([l,r])=ans([u,r])-ans([u,l-1])-[u,l-1]->[l,r]。
其中前缀ans已经预处理,剩下那个可以归并。
我们解决块间贡献可以枚举两个块然后归并,接下来设f[l,r]表示第l到r块的答案。
f[l,r]=f[l,r-1]+f[l+1,r]-f[l+1,r-1]+l->r。
因此可以预处理块间贡献。
散块间贡献可以归并得到。
现在解决散块与块间的贡献。
可以先预处理一个位置对一个块的贡献。
可以将元素按顺序插入,维护一个块内的答案,即可预处理。
散块一定是到块头或块尾的区间,再前缀和或后缀和即可。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=50000+10,B=224;
int f[B+10][B+10],num[B+10],g[maxn][B+10];
int tree[maxn];
int a[maxn],belong[maxn],b[maxn],c[maxn],d[maxn],sum[maxn],cnt[maxn];
int i,j,k,l,r,s,t,n,m,tot,top,ans,now;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp(int x,int y){
    return a[x]<a[y];
}
int lowbit(int x){
    return x&-x;
}
void change(int x){
    while (x>0){
        tree[x]++;
        x-=lowbit(x);
    }
}
int query(int x){
    int t=0;
    while (x<=n){
        t+=tree[x];
        x+=lowbit(x);
    }
    return t;
}
int work(int top,int tot){
    int i=1,j=1,t=0;
    while (i<=top&&j<=tot){
        if (c[i]<=d[j]) i++;
        else{
            j++;
            t+=top-i+1;
        }
    }
    return t;
}
int ask(int x,int l,int r){
    int i,t=sum[r]-(l==(x-1)*B+1?0:sum[l-1]);
    top=0;
    fo(i,(x-1)*B+1,min(x*B,n))
        if (b[i]<l) c[++top]=a[b[i]];
    tot=0;
    fo(i,(x-1)*B+1,min(x*B,n))
        if (b[i]>=l&&b[i]<=r) d[++tot]=a[b[i]];
    t-=work(top,tot);
    return t;
}
int main(){
    n=read();
    fo(i,1,n) a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);
    top=unique(b+1,b+n+1)-b-1;
    fo(i,1,n) a[i]=lower_bound(b+1,b+top+1,a[i])-b;
    fo(i,1,n) belong[i]=(i-1)/B+1;
    fo(i,1,n) b[i]=i;
    sort(b+1,b+n+1,cmp);
    i=1;
    while (i<=n){
        t=a[b[i]];
        j=i;
        while (i<=n&&a[b[i]]==t){
            fo(k,belong[b[i]]+1,belong[n]) g[b[i]][k]=num[k];
            i++;
        }
        fo(k,j,i-1) num[belong[b[k]]]++;
    }
    fo(i,1,belong[n]) num[i]=0;
    reverse(b+1,b+n+1);
    i=1;
    while (i<=n){
        t=a[b[i]];
        j=i;
        while (i<=n&&a[b[i]]==t){
            fo(k,1,belong[b[i]]-1) g[b[i]][k]=num[k];
            i++;
        }
        fo(k,j,i-1) num[belong[b[k]]]++;
    }
    fo(j,1,belong[n])
        fd(i,(j-1)*B,1)
            if (i%B!=0) g[i][j]+=g[i+1][j];
    fo(j,1,belong[n])
        fo(i,j*B+1,n)
            if (i%B!=1) g[i][j]+=g[i-1][j];
    fo(i,1,n){
        fo(j,1,belong[n]) g[i][j]+=g[i][j-1];
    }
    fo(j,1,belong[n]){
        fo(i,0,n) tree[i]=0;
        l=0;
        fo(i,(j-1)*B+1,min(j*B,n)){
            l+=query(a[i]+1);
            sum[i]=l;
            change(a[i]);
        }
        fo(i,0,n) tree[i]=0;
        l=0;
        fd(i,min(j*B,n),(j-1)*B+1){
            l+=query(n-a[i]+2);
            cnt[i]=l;
            change(n-a[i]+1);
        }
    }
    fo(i,1,n) b[i]=i;
    fo(i,1,belong[n]){
        l=(i-1)*B+1;r=min(i*B,n);
        sort(b+l,b+r+1,cmp);
    }
    fo(i,1,belong[n]) f[i][i]=sum[min(i*B,n)];
    fo(i,1,belong[n]-1)
        fo(j,i+1,belong[n]){
            top=0;
            fo(k,(i-1)*B+1,i*B) c[++top]=a[b[k]];
            tot=0;
            fo(k,(j-1)*B+1,min(j*B,n)) d[++tot]=a[b[k]];
            f[i][j]=work(top,tot);
        }
    fd(i,belong[n]-1,1)
        fo(j,i+1,belong[n])
            f[i][j]+=f[i][j-1]+f[i+1][j]-f[i+1][j-1];
    m=read();
    while (m--){
        j=read();k=read();
        j^=ans;k^=ans;
        l=belong[j];r=belong[k];
        if (l==r){
            ans=ask(l,j,k);
            printf("%d\n",ans);
            continue;
        }
        ans=f[l+1][r-1];
        /*ans+=ask(l,j,l*B);
        ans+=ask(r,(r-1)*B+1,k);*/
        ans+=cnt[j];
        ans+=sum[k];
        top=0;
        fo(i,(l-1)*B+1,l*B)
            if (b[i]>=j) c[++top]=a[b[i]];
        tot=0;
        fo(i,(r-1)*B+1,min(r*B,n))
            if (b[i]<=k) d[++tot]=a[b[i]];
        ans+=work(top,tot);
        /*fo(i,l+1,r-1) ans+=g[j][i];
        fo(i,l+1,r-1) ans+=g[k][i];*/
        ans+=g[j][r-1]-g[j][l];
        ans+=g[k][r-1]-g[k][l];
        printf("%d\n",ans);
    }
}

展开阅读全文
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

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

抵扣说明:

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

余额充值