[BZOJ3289]Mato的文件管理(分块+莫队+树状数组)

题目描述

传送门

题解

一段区间内交换的最小次数就是这段区间内逆序对的个数,因为只要存在一个逆序对那么它们一定会交换。
用权值树状数组动态维护逆序对,抓住”逆序对数=每一个数前面比它大的个数和=每一个数后面比它小的个数和“,再利用树状数组维护前缀和来搞。想好先统计再更新还是先更新再统计。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

const int max_n=5e4+5;

int n,m,cnt,t1,t2,ans;
int b[max_n],p[max_n],a[max_n],num[max_n],C[max_n],final[max_n];
struct hp{int l,r,ln,rn,id;}q[max_n];

inline int cmp(int x,int y){return b[x]<b[y];}
inline int cmp1(hp a,hp b){return a.ln<b.ln||a.ln==b.ln&&a.r<b.r;}
inline void add(int loc,int val){
    for (int i=loc;i<=n;i+=i&(-i))
      C[i]+=val;
}
inline int query(int loc){
    int ans=0;
    for (int i=loc;i>=1;i-=i&(-i))
      ans+=C[i];
    return ans;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&b[i]),p[i]=i;
    sort(p+1,p+n+1,cmp);
    for (int i=1;i<=n;++i)
      if (b[p[i]]!=b[p[i-1]]) a[p[i]]=++cnt; else a[p[i]]=cnt;
    int t1=(int)sqrt(n); if (n%t1==0) t2=n/t1; else t2=n/t1+1;
    for (int i=1;i<=n;++i)
      if (i%t1==0) num[i]=i/t1;
      else num[i]=i/t1+1;
    scanf("%d",&m);
    for (int i=1;i<=m;++i){
        scanf("%d%d",&q[i].l,&q[i].r); if (q[i].l>q[i].r) swap(q[i].l,q[i].r);
        q[i].ln=num[q[i].l];
        q[i].rn=num[q[i].r];
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp1);
    for (int i=q[1].r;i>=q[1].l;--i)
      ans+=query(a[i]-1),add(a[i],1);
    final[q[1].id]=ans;

    for (int i=2;i<=m;++i){
        if (q[i-1].l>q[i].l)
          for (int j=q[i-1].l-1;j>=q[i].l;--j)
            ans+=query(a[j]-1),add(a[j],1);
        if (q[i-1].l<q[i].l)
          for (int j=q[i-1].l;j<q[i].l;++j)
            ans-=query(a[j]-1),add(a[j],-1);
        if (q[i-1].r<q[i].r)
          for (int j=q[i-1].r+1;j<=q[i].r;++j){
            add(a[j],1);
            int k=query(a[j]);
            int len=j-q[i].l+1;
            ans+=len-k;
          }
        if (q[i-1].r>q[i].r)
          for (int j=q[i-1].r;j>q[i].r;--j){
            int k=query(a[j]);
            int len=j-q[i].l+1;
            ans-=len-k;
            add(a[j],-1);
          }
        final[q[i].id]=ans;
    }
    for (int i=1;i<=m;++i)
      printf("%d\n",final[i]);
}

总结

树状数组求逆序对要想好,细节比较蛋疼。

你可能感兴趣的:(树状数组,分块,bzoj,莫队)