zoukankan      html  css  js  c++  java
  • noi online 提高组t2冒泡排序

    题目描述

    给定一个 1 ∼ n1n 的排列 p_ipi,接下来有 mm 次操作,操作共两种:

    1. 交换操作:给定 xx,将当前排列中的第 xx 个数与第 x+1x+1 个数交换位置。
    2. 询问操作:给定 kk,请你求出当前排列经过 kk 轮冒泡排序后的逆序对个数。 对一个长度为 nn 的排列 p_ipi 进行一轮冒泡排序的伪代码如下:
    for i = 1 to n-1:
      if p[i] > p[i + 1]:
        swap(p[i], p[i + 1])
    

    输入格式

    第一行两个整数 nn, mm,表示排列长度与操作个数。

    第二行 nn 个整数表示排列 p_ipi

    接下来 mm 行每行两个整数 t_itic_ici,描述一次操作:

    • 若 t_i=1ti=1,则本次操作是交换操作,x=c_ix=ci
    • 若 t_i=2ti=2,则本次操作是询问操作,k=c_ik=ci

    输出格式

    对于每次询问操作输出一行一个整数表示答案。

    输入输出样例

    输入 #1
    3 6
    1 2 3
    2 0
    1 1
    1 2
    2 0
    2 1
    2 2
    输出 #1
    0
    2
    1
    0

    说明/提示

    样例一解释

    第一次操作:排列为 {1,2,3},经过 0 轮冒泡排序后为 {1,2,3}0 个逆序对。

    第二次操作:排列变为 {2,1,3}

    第三次操作:排列变为 {2,3,1}

    第四次操作:经过0轮冒泡排序后排列变为 {2,3,1}{2,3,1},2 个逆序对。

    第五次操作:经过1轮冒泡排序后排列变为 {2,1,3}{2,1,3},1 个逆序对。

    第六次操作:经过2轮冒泡排序后排列变为 {1,2,3}{1,2,3},0 个逆序对。


    数据范围与提示

    对于测试点 1 ∼ 2:nn, m leqslant 100m100。

    对于测试点 3 ∼ 4:nn, m leqslant 2000m2000。

    对于测试点 5 ∼ 6:交换操作个数不超过 100100。

    对于所有测试点:2 leqslant n2n, m leqslant 2 imes 10^5m2×105,t_i in {1,2}ti{1,2},1 leqslant x < n1x<n,0 leqslant k < 2^{31}0k<231。

    解析:

    本题是个结论题。。。

    手玩几组冒泡排序就会发现,经历一次冒泡排序会发生这样的事情:

    如果一个数左边有比它大的数:这个数消除不了逆序对

    否则,这个数可以一直向后交换,直到遇到比它大的数

    简单理解:如果它左边有比它厉害的,它就被干掉了,

    否则它就一直向右碾压,直到遇到它打不过的人再停下。

    具体操作:

    我们记录第i位数前面比它大的数的数量为b[i],显然,当前序列的总逆序对数量就是所有的b之和通过对冒泡排序的观察,我们可以发现,每一遍冒泡排序都会使得所有b[i]=max(b[i]-1,0)

     我们采用树状数组差分维护这一操作,令quera(t)为当ti=2k=t时的答案

    在数组最前面加入当前序列总逆序对数量,然后在第i位放b大于i的数字的数量的相反数,

    因为这些数字在第i轮逆序对数均会减1

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=2e5+5;
    int n,m,a[maxn],b[maxn],d[maxn];
    ll c[maxn],ans;
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    inline void add(int x,ll val)
    {
        while(x<=n)
    	{
            c[x]+=val;
            x+=lowbit(x);
        }
    }
    inline ll quera(int x)
    {
        ll res=0;
        while(x>0)
    	{
            res+=c[x];
            x-=lowbit(x);
        }
        return res;
    }
    int main()
    {
        int opt,x,tmp=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
    	{
            scanf("%d",&a[i]);
            b[i]=i-1-quera(a[i]);
            ans+=b[i],++d[b[i]];
            add(a[i],1);
        }
        memset(c,0,sizeof(c));
        add(1,ans);
        for(int i=0;i<n;i++)
    	{
            tmp+=d[i];
            add(i+2,tmp-n);
        }
        for(int i=1;i<=m;i++)
    	{
            scanf("%d%d",&opt,&x);
            x=min(x,n-1);
            if(opt==1)
    		{
                if(a[x]<a[x+1])
    			{
                    swap(a[x],a[x+1]);
                    swap(b[x],b[x+1]);
                    add(1,1);
                    add(b[x+1]+2,-1);
                    b[x+1]++;
                }
                else
    			{
                    swap(a[x],a[x+1]);
                    swap(b[x],b[x+1]);
                    add(1,-1);
                    b[x]--;
                    add(b[x]+2,1);
                }
            }
            else printf("%lld
    ",quera(x+1));
        }
        return 0;
    }
    

      

  • 相关阅读:
    Three.js 类的粗略总结和实现
    JavaScript方法
    JavaScript 继承和数组
    JavaScript 函数
    操作系统之堆和栈的区别
    第十二章 动态内存
    第十三章-第六小节-对象移动
    第十五章 面形对象程序设计
    动态建树和静态建树
    高斯消元整数版和浮点数版实现
  • 原文地址:https://www.cnblogs.com/chen-1/p/12442073.html
Copyright © 2011-2022 走看看