计蒜客模拟赛D2T2 蒜头君的排序:区间逆序对(移动端点) + 树状数组
题目链接:https://nanti.jisuanke.com/t/16443
题意:
给你一个由1~n构成的正整数序列,有m组询问,每组询问要求输出[l , r]区间内的逆序对个数。
数据范围:
对于100%的数据,满足1 <= n,m <= 30000,l < r,∑ | l[i] - l[i-1] | + ∑ | r[i] - r[i-1] | <= 7 * 10^6。
题解:
如果知道区间[l , r]中的逆序对个数,那么也可以快速求出区间[l-1 , r],[l+1 , r],[l , r-1],[l , r+1]的逆序对个数。
① [l-1 , r]的个数 = [l , r]的个数 + [l , r]中比a[l-1]小的元素的个数
② [l+1 , r]的个数 = [l , r]的个数 - [l+1 , r]中比a[l]小的元素的个数
③ [l , r-1]的个数 = [l , r]的个数 - [l , r-1]中比a[r]大的元素的个数
④ [l , r+1]的个数 = [l , r]的个数 + [l , r]中比a[r+1]大的元素的个数
查询区间内比x大(小)的元素的个数用树状数组实现。
比x大的元素个数 = query(x - 1)
比x小的元素个数 = query(n) - query(x) (区间内元素总个数减去大于等于x的元素个数)
先假定现在的区间为[1 , 1],逆序对总个数为0。对于每一组询问,只要不断移动当前区间的左右端点,并同时更新答案,直至当前区间与询问区间相同即可,输出sum值。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 30005 5 6 using namespace std; 7 8 int n,m; 9 int a[MAX_N]; 10 int dat[MAX_N]; 11 12 void update(int k,int a) 13 { 14 while(k<=n) 15 { 16 dat[k]+=a; 17 k+=k&-k; 18 } 19 } 20 21 int query(int k) 22 { 23 int sum=0; 24 while(k>0) 25 { 26 sum+=dat[k]; 27 k-=k&-k; 28 } 29 return sum; 30 } 31 32 void read() 33 { 34 cin>>n; 35 for(int i=1;i<=n;i++) 36 { 37 cin>>a[i]; 38 } 39 } 40 41 void solve() 42 { 43 memset(dat,0,sizeof(dat)); 44 cin>>m; 45 int sum=0; 46 int lef=1,rig=1; 47 update(a[1],1); 48 for(int i=0;i<m;i++) 49 { 50 int l,r; 51 cin>>l>>r; 52 while(lef<l) 53 { 54 update(a[lef],-1); 55 sum-=query(a[lef]-1); 56 lef++; 57 } 58 while(lef>l) 59 { 60 lef--; 61 sum+=query(a[lef]-1); 62 update(a[lef],1); 63 } 64 while(rig<r) 65 { 66 rig++; 67 sum+=query(n)-query(a[rig]); 68 update(a[rig],1); 69 } 70 while(rig>r) 71 { 72 update(a[rig],-1); 73 sum-=query(n)-query(a[rig]); 74 rig--; 75 } 76 cout<<sum<<endl; 77 } 78 } 79 80 int main() 81 { 82 read(); 83 solve(); 84 }