【洛谷6186】[NOI Online #1 提高组] 冒泡排序(树状数组)

点此看题面

  • 给定一个长度为(n)的排列,有两种操作:
    • 交换一对相邻的数。
    • 询问(k)轮冒泡排序之后的逆序对个数。
  • (n,qle2 imes 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
",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;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6186.html