【BZOJ3289】Mato的文件管理,莫队+树状数组

Time:2016.09.07
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
这个题意就是让你求[l,r]的逆序对数
暴力做的话是 O(Qn2) O(Qnlogn)
考虑莫队做法
那怎么在莫队转移的时候快速计算逆序对数呢?
如果我们当前的区间是[l,r]
转移[l+1,r]—>减去的答案就是[l,r]中比a[l]小的数的个数
转移[l-1,r]—>加上的答案就是[l,r]中比a[l-1]小的数的个数
转移[l,r+1]—>加上的答案就是[l,r]中比a[r+1]大的数的个数
转移[l,r-1]—>减去的答案就是[l,r]中比a[r]大的数的个数
这个东西我们可以用离散化+树状数组来搞啊
这样的话复杂度就是 O(nnlogn)
比较玄学的复杂度
注意离散化后树状数组的区间并不是[1,n]
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath> 
#define low(x) (x&-x)
#define M 50003
using namespace std;
int n,m;
int a[M],b[M],c[M],d[M],block[M];
struct query{
    int l,r,id;
}q[M];
bool cmp(query a,query b)
{
    if (block[a.l]==block[b.l]) return a.r<b.r;
    return block[a.l]<block[b.l];
}
int in()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
void add(int x,int val)
{
    for (;x<=b[0];x+=low(x))
        d[x]+=val; 
}
int get(int x)
{
    int ans=0;
    for (;x;x-=low(x))
        ans+=d[x];
    return ans;
}
main()
{
    n=in();
    int t=sqrt(n);
    for (int i=1;i<=n;++i)
        a[i]=b[i]=in(),
        block[i]=i/t+1;
    sort(b+1,b+n+1);
    b[0]=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
    m=in();
    for (int i=1;i<=m;++i) q[i]=(query){in(),in(),i};
    sort(q+1,q+m+1,cmp);
    int L=1,R=0,ans=0;
    for (int i=1;i<=m;++i)
    {
        for (int j=R+1;j<=q[i].r;++j)
            ans+=get(b[0])-get(a[j]),
            add(a[j],1);
        for (int j=R;j>q[i].r;--j)
            ans-=get(b[0])-get(a[j]),
            add(a[j],-1);
        for (int j=L;j<q[i].l;++j)
            ans-=get(a[j]-1),
            add(a[j],-1);
        for (int j=L-1;j>=q[i].l;--j)
            ans+=get(a[j]-1),
            add(a[j],1);
        L=q[i].l;R=q[i].r;
        c[q[i].id]=ans;
    }
    for (int i=1;i<=m;++i) printf("%d\n",c[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值