【洛谷6186】[NOI Online #1 提高组] 冒泡排序(树状数组)
- 给定一个长度为\(n\)的排列,有两种操作:
- 交换一对相邻的数。
- 询问\(k\)轮冒泡排序之后的逆序对个数。
- \(n,q\le2\times 10^5\)
简单的结论题
记录\(c_i\)表示第\(i\)个数左侧有多少比它大的数。
则一轮冒泡排序相当于是从左到右把每个\(c_i=0\)的数移到下一个\(c_i=0\)的数左边。
除此之外,所有数左侧都会有某个比它大的数移到了它的右侧,因此\(c_i\)减\(1\)。
那么对于一次询问相当于就是求出\(\sum_{i=1}^{n}\max\{c_i-k,0\}\)。
这等同于所有大于等于\(k\)的\(c_i\)之和减去大于等于\(k\)的\(c_i\)个数乘\(k\)。
发现一次修改实际上只会影响到交换的这两个位置的\(c_i\),因此就是单点修改。
那么只要维护一个权值树状数组即可。
代码:\(O(nlogn)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define LL long long
using namespace std;
int n,a[N+5],c[N+5];
struct TreeArray
{
LL a[N+5];I void U(RI x,CI v) {++x;W(x) a[x]+=v,x-=x&-x;}//单点修改
I LL Q(RI x,LL t=0) {++x;W(x<=n+1) t+=a[x],x+=x&-x;return t;}//后缀求和
}A,T1,T2;
int main()
{
#define T(x,v) (T1.U(x,v),T2.U(x,v*x))
RI Qt,i;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i) scanf("%d",a+i),c[i]=A.Q(a[i]),A.U(a[i],1),T(c[i],1);//求出初始c[i]
RI op,x;W(Qt--) scanf("%d%d",&op,&x),op==2?printf("%lld\n",T2.Q(x)-T1.Q(x)*x):(T(c[x],-1),T(c[x+1],-1),//操作2直接查询;操作1先清除原贡献
a[x]>a[x+1]&&--c[x+1],swap(a[x],a[x+1]),swap(c[x],c[x+1]),a[x]>a[x+1]&&++c[x+1],T(c[x],1),T(c[x+1],1),0);//交换,然后把贡献加回去
return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒