BZOJ 3289 Mato的文件管理

本文介绍了一种使用树状数组求解区间逆序对的方法,并结合莫队算法处理多个查询请求。通过实例讲解了如何实现这一算法,并分享了作者在编写过程中的心得与调试经验。

题目

http://www.lydsy.com/JudgeOnline/problem.php?id=3289

题解

实际上就是求区间逆序对个数,第一次写树状数组求逆序对,不过感觉还是比归并排序好些。(其实感觉归并排序比较难写),因为有很多询问,所以离线搞,然后用莫队算法(第一次写莫队,感觉好神奇,还不很明白复杂度的保证)。


还有不能偷懒啊,,我感觉这份代码刚写完的时候应该有10+处bug

代码

/**************************************************************
    Problem: 3289
    User: QWsin
    Language: C++
    Result: Accepted
    Time:8864 ms
    Memory:3456 kb
****************************************************************/

// QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=50000+10;
const int maxa=50000+10;
inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret;
}

int n,m,a[maxn],sz[maxn];
ll C[maxn],ans[maxn];

struct Ask{
    int l,r,kid,id;
    inline void input(int i){
        l=read();r=read();id=i;
    }
    bool operator <(const Ask &rhs)const{
        if(kid!=rhs.kid) return kid<rhs.kid;
        return r<rhs.r;
    }
}ask[maxa];

int cmp(Ask a,Ask b){return a.l<b.l;}

#define lowbit(x) ((x)&(-x))
inline void updata(int p,int x){
    for(;p<=n;p+=lowbit(p)) C[p]+=x;
}

inline ll query(int p)
{
    ll ret=0;
    for(;p;p-=lowbit(p)) ret+=C[p];
    return ret;
}

int id[maxn];

int main()
{
    int N;cin>>N;
    for(int i=1;i<=N;i++) sz[i]=a[i]=read();
    sort(sz+1,sz+N+1);
    n=unique(sz+1,sz+N+1)-sz-1;
    for(int i=1;i<=N;i++) id[i]=lower_bound(sz+1,sz+n+1,a[i])-sz;

    cin>>m;
    for(int i=1;i<=m;i++) ask[i].input(i);

    sort(ask+1,ask+m+1,cmp);
    int k=sqrt(m);
    for(int i=1;i<=m;i++) ask[i].kid=(i-1)/k+1;
    sort(ask+1,ask+m+1);

    int L=ask[1].l,R=ask[1].r;
    ll Ans=0;
    for(int i=ask[1].l;i<=ask[1].r;i++)
    {
        Ans+=query(n)-query(id[i]);
        updata(id[i],1);
    }
    ans[ask[1].id]=Ans;

    for(int i=2;i<=m;i++)
    {
        int r=ask[i].r,l=ask[i].l;
        while(R<r) 
        {
            Ans+=query(n)-query(id[++R]);
            updata(id[R],1);
        }
        while(R>r) 
        {
            Ans-=query(n)-query(id[R]);
            updata(id[R--],-1);
        }
        while(L<l) 
        {
            Ans-=query(id[L]-1);
            updata(id[L++],-1);
        }
        while(L>l)
        {
            Ans+=query(id[--L]-1);
            updata(id[L],1);
        }
        ans[ask[i].id]=Ans;
    }

    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值