首页 > 编程语言 > 详细

习题:冒泡排序(逆序对&权值线段树)

时间:2020-03-07 23:23:25      阅读:107      评论:0      收藏:0      [点我收藏+]

题目

传送门

思路

不好想到

定义t数组,其中\(t_i\)表示在\(1\le j<i\)中有多少个大于\(a_i\)\(a_j\)

那么逆序对就是\(\sum_{i=1}^n{t_i}\)

考虑一操作

很容易就可以维护

再考虑二操作

注意到一个性质

一轮冒泡排序会让\(t_i=max(t_i-1,0)\)

这很显然,因为一轮冒泡只会让\(t_i\)左边一个大于\(a_i\)的数跑到\(a_i\)右边

用权值线段树就可以维护

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
struct node
{
    int l;
    int r;
    int tot;
    long long s;
}tre[1000005];
int n,m;
int a[200005];
int b[200005];
long long bit[200005];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int val)
{
    for(int i=x;i<=n;i+=lowbit(i))
        bit[i]+=val;
}
long long solve_s(int x)
{
    long long ret=0;
    for(int i=x;i>0;i-=lowbit(i))
        ret+=bit[i];
    return ret;
}   
void build(int l,int r,int k)
{
    //cout<<k<<'\n';
    tre[k].l=l;
    tre[k].r=r;
    tre[k].s=0;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}
void add(int a,int f,int k)
{
    if(tre[k].l>a||a>tre[k].r)
        return;
    if(tre[k].l==tre[k].r)
    {
        tre[k].tot+=f;
        tre[k].s=tre[k].s+tre[k].l*f;
        return;
    }
    add(a,f,k<<1);
    add(a,f,k<<1|1);
    tre[k].tot=tre[k<<1].tot+tre[k<<1|1].tot;
    tre[k].s=tre[k<<1].s+tre[k<<1|1].s;
}
long long ask(int l,int r,int k)
{
    if(l>r)
        return 0;
    if(l>tre[k].r||tre[k].l>r)
        return 0;
    if(l<=tre[k].l&&tre[k].r<=r)    
        return tre[k].s-1ll*tre[k].tot*(l-1);
    return ask(l,r,k<<1)+ask(l,r,k<<1|1);
}
int main()
{
    //freopen("bubble.in","r",stdin);
    //freopen("bubble.out","w",stdout); 
    scanf("%d %d",&n,&m);
    build(0,n,1);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=i-1-solve_s(a[i]);
        update(a[i],1);
        add(b[i],1,1);
    }
    for(int i=1;i<=m;i++)
    {
        int t;
        long long c;
        scanf("%d %lld",&t,&c);
        if(t==1)
        {
            if(a[c]>a[c+1])
            {
                add(b[c],-1,1);
                add(b[c+1],-1,1);
                int x=b[c];
                int y=b[c+1];
                b[c]=y-1;
                b[c+1]=x;
                add(b[c],1,1);
                add(b[c+1],1,1);
                swap(a[c],a[c+1]);
            }
            else if(a[c]<a[c+1])
            {
                add(b[c],-1,1);
                add(b[c+1],-1,1);
                int x=b[c];
                int y=b[c+1];
                b[c]=y;
                b[c+1]=x+1;
                add(b[c],1,1);
                add(b[c+1],1,1);
                swap(a[c],a[c+1]);
            }
        }
        else
        {
            printf("%lld\n",ask(c+1,n,1));
        }
    }
    return 0;
}

习题:冒泡排序(逆序对&权值线段树)

原文:https://www.cnblogs.com/loney-s/p/12439103.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!