莫队+树状数组 题解【bzoj3289】Mato的文件管理

本文介绍了一种使用莫队算法和树状数组解决区间逆序对问题的方法。通过将问题离散化并分块处理,实现了从O(n^2)到O(n^1.5)的时间复杂度优化。文章详细讲解了代码实现过程,并提供了完整的C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先题目意思很简单
就是求每一段区间内的逆序对个数 在我认知中有两种求逆序对的方法 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值