蒜头君的排序
蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺激的,给定一个 1 \ldots n1…n 的排列,每次从该排列中选择一个区间 [l,r][l,r],问使用冒泡排序将该区间排至升序需要多少次交换操作。
输入格式
第一行一个整数 nn,表示排列长度。
接下来一行 nn 个整数,表示该排列。
接下来一行一个整数 mm,表示询问次数。
接下来 mm 行,每行 22 个整数 l,rl,r,表示询问 [l,r][l,r] 区间。
输出格式
输出 mm 行,每行 11 个整数,第 ii 行表示第 ii 个询问的答案。
数据规模
样例输入
10 9 8 7 4 5 6 10 3 2 1 5 2 4 8 10 2 8 5 9 4 9
样例输出
3 3 13 7 9
题解:
动态维护的树状数组
一开始很纠结到底怎么动态维护逆序对,之后看了数据才恍然大悟
最后一个范围意思就是左右端点的变化量<=7*10^6
这样连莫队都不需要了,直接左右端点移动就行了
区间[l,r]很容易堆出区间[l-1,r],[l+1,r],[l,r-1],[l,r+1]
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int c[300001],a[300001],rpos=0,lpos=1,n,m,ans; 7 void add(int x,int d) 8 { 9 while (x<=n) 10 { 11 c[x]+=d; 12 x+=(x&(-x)); 13 } 14 } 15 int query(int x) 16 { 17 int s=0; 18 while (x) 19 { 20 s+=c[x]; 21 x-=(x&(-x)); 22 } 23 return s; 24 } 25 int main() 26 {int i,j,l,r; 27 cin>>n; 28 for (i=1;i<=n;i++) 29 { 30 scanf("%d",&a[i]); 31 } 32 cin>>m; 33 for (i=1;i<=m;i++) 34 { 35 scanf("%d%d",&l,&r); 36 while (rpos<r) 37 { 38 rpos++; 39 ans+=query(n)-query(a[rpos]-1); 40 add(a[rpos],1); 41 } 42 while (rpos>r) 43 { 44 add(a[rpos],-1); 45 ans-=query(n)-query(a[rpos]-1); 46 rpos--; 47 } 48 while (lpos<l) 49 { 50 add(a[lpos],-1); 51 ans-=query(a[lpos]-1); 52 lpos++; 53 } 54 while (lpos>l) 55 { 56 lpos--; 57 ans+=query(a[lpos]-1); 58 add(a[lpos],1); 59 } 60 printf("%d\n",ans); 61 } 62 }