[NOI Online 提高组]冒泡排序

刚考完,赶紧来水一波题解

一、题目

点此看题

二、解法

首先这道题有一道同名题,很相似(推荐先看这道同名题的题解,那题是黑的,怎么这题评成蓝色了?)

逆序对转化成每一个数前面比它大的数的个数(记为 b b b数组),如果一个数前面有比他大的数,那么它会左移一位,也就是 b b b 1 1 1,那么答案为(记轮数为 k k k):
∑ min ⁡ ( b [ i ] − k , 0 ) \sum \min(b[i]-k,0) min(b[i]k,0)

很容易发现用权值线段树维护吧,分成大于 k k k的部分和小于等于 k k k的部分,算大于 k k k部分的贡献即可,至于修改,只交换 x x x x + 1 x+1 x+1,显然对其它的位置是没有任何影响的,讨论一下然后在权值线段树上修改就行了。

图方便我写的树状数组,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),代码在民间数据能够通过,并且我这个小蒟蒻还是觉得打的是对的,可以放心看 q w q qwq qwq

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define ll long long
const int M = 200005;
int read()
{
    int x=0,flag=1;char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,a[M],b[M],bit[M];
ll k,sum[M],A,B;
int lowbit(int x)
{
    return x&(-x);
}
void upd(int x)
{
    for(int i=x;i<=n;i+=lowbit(i))
        bit[i]++;
}
int ask(int x)
{
    int res=0;
    for(int i=x;i>=1;i-=lowbit(i))
        res+=bit[i];
    return res;
}
void modify(int x,int f)
{
    for(int i=x+1;i<=n;i+=lowbit(i))
    {
        bit[i]+=f;
        sum[i]+=x*f;
    }
}
void query(int x)
{
    for(int i=x+1;i>=1;i-=lowbit(i))
    {
        A+=bit[i];
        B+=sum[i];
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        b[i]=i-ask(a[i])-1;
        k+=b[i];
        upd(a[i]);
    }
    memset(bit,0,sizeof bit);
    for(int i=1;i<=n;i++)
    {
        modify(b[i],1);
    }
    for(int i=1;i<=m;i++)
    {
        int op=read(),x=read();
        if(op==1)
        {
            if(a[x]>a[x+1])
            {
                swap(a[x],a[x+1]);
                swap(b[x],b[x+1]);
                modify(b[x],-1);
                b[x]--;k--;
                modify(b[x],1);
            }
            else if(a[x]<a[x+1])
            {
                swap(a[x],a[x+1]);
                swap(b[x],b[x+1]);
                modify(b[x+1],-1);
                b[x+1]++;k++;
                modify(b[x+1],1);
            }
        }
        else
        {
            if(x>=n)
            {
                puts("0");
                continue ;
            }
            A=0;B=0;
            query(x);
            ll ans=(k-B)-(n-A)*x;
            printf("%lld\n",ans);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值