询问区间逆序对,强制在线。
分块。
简单的来说就是预处理每个点到块首和块尾的贡献,还有块与块之间的贡献,还有前 i 个块对于值 j 的贡献。
询问的时候直接调用预处理的答案并归并两边散块即可。
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool Cnt=false;
while(!isdigit(ch)){if(ch=='-'){Cnt=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=Cnt?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=1e5+5,siz=317,B=320;
struct BIT{
int c[N];
inline void Add(int i,int x){for(;i<N;i+=i&-i)c[i]+=x;}
inline int Ask(int i){int res=0;for(;i;i^=i&-i)res+=c[i];return res;}
}t;
int n,m,a[N],L[B],R[B],pos[N],blocks,pre[N],suf[N],Cnt[B][N];
int x[B],y[B],posl,posr,c[N],d[N];
long long Num[B][B],las;
pair<int,int> b[N];
inline int Merge(int*a,int*b,int nl,int nr){//归并
int posl=1,posr=1,res=0;
while(posl<=nl&&posr<=nr){
if(a[posl]<b[posr]) ++posl;
else res+=nl-posl+1,++posr;
}
return res;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;i++) read(a[i]);
blocks=(n-1)/siz+1;
for(int i=1;i<=blocks;i++) L[i]=R[i-1]+1,R[i]=i*siz;
R[blocks]=n;
for(int i=1;i<=n;i++) b[i]={a[i],i};
for(int i=1;i<=blocks;i++){//预处理块内每一个位置到块首/块尾的逆序对个数
sort(b+L[i],b+R[i]+1);
for(int j=L[i];j<=R[i];j++) pos[j]=i,c[j]=b[j].first,d[j]=b[j].second;
int x=0;
for(int j=L[i];j<=R[i];j++){
t.Add(a[j],1);
x+=t.Ask(n)-t.Ask(a[j]);
pre[j]=x;
}
Num[i][i]=x;
for(int j=L[i];j<=R[i];j++){
suf[j]=x;
t.Add(a[j],-1);
x-=t.Ask(a[j]-1);
}
}
sort(b+1,b+n+1);
for(int j=1;j<=blocks;j++){//预处理前 i 个块中 小于等于/大于等于 j 的个数
for(int i=1,k=L[j];i<=n;i++){
const int id=b[i].second;
while(k<=R[j]&&b[i].first>c[k])++k;
if(id<L[j]) Cnt[j][id]=k-L[j];
else if(id>R[j]) Cnt[j][id]=R[j]-k-(k<=R[j]&&b[i].first==c[k])+1;
}
}
for(int i=1;i<=blocks;i++) for(int j=2;j<=n;j++) Cnt[i][j]+=Cnt[i][j-1];
for(int len=1;len<=blocks;len++){//预处理第 i 个块到第 j 个块的答案
for(int i=1;i<=blocks;i++)
if(len+i>blocks) break;
else{
const int j=i+len;
Num[i][j]=Num[i+1][j]+Num[i][j-1]-Num[i+1][j-1]+Cnt[j][R[i]]-Cnt[j][L[i]-1];
}
}
while(m--){
long long ql,qr;
read(ql),read(qr);
const int l=ql^las,r=qr^las,p=pos[l],q=pos[r];
posl=posr=0;
if(p==q){//如果在同一块,直接暴力归并
for(int i=L[p];i<=R[p];i++){
if(l<=d[i]&&d[i]<=r) y[++posr]=c[i];
else if(d[i]<l) x[++posl]=c[i];
}
las=pre[r]-((l==L[p])?(0):(pre[l-1]))-Merge(x,y,posl,posr);
}
else{
las=Num[p+1][q-1]+pre[r]+suf[l];//先加上预处理的答案 :中间和中间,两边角内部
for(int i=p+1;i<q;i++) las+=(Cnt[i][R[p]]-Cnt[i][l-1])+(Cnt[i][r]-Cnt[i][L[q]-1]);//中间和两边角的贡献
for(int i=L[p];i<=R[p];i++) if(d[i]>=l) x[++posl]=c[i];//每个块的d数组是有序的
for(int i=L[q];i<=R[q];i++) if(d[i]<=r) y[++posr]=c[i];
las+=Merge(x,y,posl,posr);//两边角之间归并暴力计算答案
}
write(las),putchar('\n');
}
return 0;
}