zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
  • 相关阅读:
    在未排序的数组中找到第 k 个最大的元素
    区域和检索
    控制台画图程序(可更换笔刷版本)
    循环中的scanf处理了换行符怎么破
    strlen获取字符数组为什么是255
    宽字符输出中文,Devc++解决方法
    区间取最小值最大值-位值和
    模拟鼠标键盘-封装函数
    scanf("%d",a[i]+j)为什么不加取地址符号
    scanf需要多输入一行是什么问题
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6186.html
Copyright © 2011-2022 走看看