首先题目意思很简单
就是求每一段区间内的逆序对个数 在我认知中有两种求逆序对的方法 1.归并排序可以求逆序对 2.树状数组求逆序对 修改和查询都是O(logn)的复杂度
本题要不断修改逆序对数据结构只能选择树状数组
然后 就往莫队上去套咯 离线求解 分块 然后按左端点所在的块排序 再按后端点排序
定义一个L,R,不断++,–,update树状数组和更新ans就好了 不过感觉有点抽象 一个小bug调了20多分钟
莫队嘛 反正就是很神奇的暴力 不用多想
注意要离散 因为不知道数据范围
用了莫队就把n^2优化到了n^1.5是不是很神奇呢 其实我也这么觉得 非常神奇
当然这只是最基础的莫队 还有高级一点的莫队要在树上进行 以后有机会再写
大家先看懂这个莫队
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<vector>
#define M 50005
#define ll long long
using namespace std;
struct node{
int l,r,id;
int ans;
}Q[M];
int ans,n,m,block[M],A[M],c[M];
int sum(int i){
int res=0;
while(i){
res+=c[i];
i-=i&-i;
}
return res;
}
void update(int i,int x){
while(i<=n){
c[i]+=x;
i+=i&-i;
}
}
bool cmp(node a,node b){
if(block[a.l]!=block[b.l])return block[a.l]<block[b.l];
return a.r<b.r;
}//按块来排序
bool cmp1(node a,node b){
return a.id<b.id;
}//最后输出的时候还是要按顺序输出的嘛 按id排个序就好了 \(^o^)/~
void Rd(int &res){
char c;res=0;
while(c=getchar(),!isdigit(c));
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),isdigit(c));
}//读入挂 可以自行忽略
void init(){
Rd(n);
int limit=sqrt(n);
int B[M];
for(int i=1;i<=n;i++){
Rd(A[i]);
B[i]=A[i];
block[i]=(i-1)/limit+1;
}
sort(B+1,B+n+1);//以下几行都是离散操作 sort unique lower_bound
int k=unique(B+1,B+n+1)-B-1;
for(int i=1;i<=n;i++)A[i]=lower_bound(B+1,B+k+1,A[i])-B;
for(int i=1;i<=n;i++){
update(A[i],1);
ans+=i-sum(A[i]);//先初始化ans 即逆序对的个数
}
// printf("逆序对=%d\n",ans);
Rd(m);
for(int i=1;i<=m;i++){
Rd(Q[i].l);Rd(Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+m+1,cmp);
}
void solve(){
int l=1,r=n;//l和r都在最左和最右 然后ans也已经初始化好了 接下来就要开始快乐的暴力环节
for(int i=1;i<=m;i++){//update更新环节需要自己参悟了 也不能帮太多 这个挺好推的 时刻更新树状数组和ans就好
while(l<Q[i].l){
ans-=sum(A[l]-1);
update(A[l],-1);
l++;
}
while(l>Q[i].l){
l--;
update(A[l],1);
ans+=sum(A[l]-1);
}
while(r<Q[i].r){
r++;
update(A[r],1);
ans+=r-l+1-sum(A[r]);
}
while(r>Q[i].r){
ans-=r-l+1-sum(A[r]);
update(A[r],-1);
r--;
}
Q[i].ans=ans;//最后挪到了要求访问的区间 就把ans记录下来
}
sort(Q+1,Q+m+1,cmp1);
for(int i=1;i<=m;i++)printf("%d\n",Q[i].ans);//输出答案
}
int main(){
init();
solve();
return 0;
}