您的位置:首页 > 其它

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

2016-04-26 17:04 549 查看

题目描述

传送门

题解

一段区间内交换的最小次数就是这段区间内逆序对的个数,因为只要存在一个逆序对那么它们一定会交换。

用权值树状数组动态维护逆序对,抓住”逆序对数=每一个数前面比它大的个数和=每一个数后面比它小的个数和“,再利用树状数组维护前缀和来搞。想好先统计再更新还是先更新再统计。

代码

#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]);
}


总结

树状数组求逆序对要想好,细节比较蛋疼。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: