【洛谷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;
}
posted @ 2021-01-09 18:43  TheLostWeak  阅读(25)  评论(0编辑  收藏