bzoj 3289 : Mato的文件管理 (莫队+树状数组)
题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=3289
思路:
求区间最小交换的次数将区间变成一个不降序列其实就是求区间逆序对的数量,这道题的样例解释可以不看,样例应该是
4和2换再和3换,这样就是最小的交换次数变成不降序列,从样例我们可以看出其实这就是求逆序对的过程,但是这道题是
区间询问逆序对的个数,我们需要离线处理所有询问,然后用树状数组维护数组中比这个数小的数的数量,用莫队逐个维护
就好了,
我们可以推出删除,增加一个数对当前区间逆序对数量变化的关系:
1. 在序列的前端添加一个数,那么序列逆序对的数量就会增加序列中比这个数小的数的数量
2.在序列的前端删除一个数,那么序列中逆序对的数量就会减少序列中比这个数小的数的数量
3.在序列的末端添加一个数,那么序列中逆序对的数量就会增加序列中比这个数大的数的数量
4.在序列的末端删除一个数,那么序列中逆序对的数量就会增加序列中比这个数大的数的数量
这里树状数组的作用就是维护序列中比某个数大/小的数的数量
实现代码;
#include<bits/stdc++.h> using namespace std; const int M = 1e5+10; int blo,n,m,a[M],b[M],c[M<<2],num[M]; int lowbit(int x){ return x&(-x);} int getsum(int x){ int sum = 0; while(x>0){ sum += c[x]; x -= lowbit(x); } return sum; } void update(int x,int val){ while(x <= n){ c[x] += val; x += lowbit(x); } } struct node{ int l,r,id; }q[M]; bool cmp(node a,node b){ if(a.l/blo == b.l/blo) return a.r < b.r; return a.l < b.l; } int main() { scanf("%d",&n); blo = sqrt(n); for(int i = 1;i <= n;i ++){ scanf("%d",&a[i]); b[i] = a[i]; } sort(b+1,b+n+1); for(int i = 1;i <= n;i ++) a[i] = lower_bound(b+1,b+1+n,a[i])-b; scanf("%d",&m); for(int i = 1;i <= m;i ++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].id = i; } sort(q+1,q+1+m,cmp); int l = 1,r = 0,ans = 0; for(int i = 1;i <= m;i ++){ while(l < q[i].l) update(a[l],-1),ans -= getsum(a[l]-1),l++; while(r < q[i].r) r++,ans += getsum(n)-getsum(a[r]),update(a[r],1); while(l > q[i].l) l--,ans += getsum(a[l]-1),update(a[l],1); while(r > q[i].r) update(a[r],-1),ans -= getsum(n) - getsum(a[r]),r--; num[q[i].id] = ans; } for(int i = 1;i <= m;i ++) printf("%d\n",num[i]); return 0; }