【模板】逆序对

本文介绍了如何运用权值线段树状数组来求解正整数序列中的逆序对数量。文章通过洛谷平台上的题目为例,详细解释了逆序对的概念,并给出了数据范围。在内容中,作者分享了学习权值线段树状数组的心得,并暗示了这种算法在处理大规模数据时的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天本蒟蒻终于学会了一个提高-的算法。
传送门:(洛谷)逆序对


题意

    对于给定的一段正整数序列,逆序对就是序列中ai>aj且i< j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。


数据范围

对于50%的数据,n≤2500
对于100%的数据,n≤40000。


代码

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e5+10;

int a[N],t[N],n;

inline ll merge(int l,int mid,int r)
{
    int i=l,j=mid+1,k=0;
    ll an=0;
    while(i<=mid && j<=r){
        if(a[i]>a[j]){
            an+=(mid-i+1);//this step 
            t[k++]=a[j++];
        }else{
            t[k++]=a[i++];
        }
    }
    while(i<=mid){
       t[k++]=a[i++];
    } 
    while(j<=r) t[k++]=a[j++];
    for(int g=0;g<k;g++) a[l+g]=t[g];
    return an;
}

inline ll query(int l,int r){
    ll ans=0;
    if(l<r){
        int mid=(l+r)>>1;
        ans+=query(l,mid);
        ans+=query(mid+1,r);
        ans+=merge(l,mid,r);
    }
    return ans;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%d",&a[i]);
    printf("%lld\n",query(1,n));
    return 0;
} 

今天(2018.3.30)学会了权值线段树状数组:

#include<bits/stdc++.h>
#define Rg register 
using namespace std;
const int N=1e5+10;
typedef long long ll;
int tot[N<<2],mx,tg[N];
int n;ll ans;

struct P{
    int v,um;
    bool operator< (const P& u)const{
      return v<u.v;
    }
}a[N];

inline int read()
{
    char c=getchar();int x=0;
    while(c<'0' || c>'9') {c=getchar();}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48));c=getchar();}
    return x;
}

inline int get(int k)
{
    int ss=0;
    for(;k>0;k-=(k&(-k))){ss+=tot[k];}
    return ss;
}

inline void update(int k,int val)
{
    for(;k<=mx;k+=(k&(-k))){tot[k]+=val;}
}

int main(){
    n=read();
    for(Rg int x,i=1;i<=n;i++){
       a[i].v=read();
       a[i].um=i;
    }
    sort(a+1,a+n+1);
    for(Rg int i=1;i<=n;i++){
        if(a[i].v!=a[i-1].v){mx++;}
        tg[a[i].um]=mx;
    }
    mx++;
    update(tg[1],1);
    for(Rg int i=2;i<=n;i++){
        ans+=(i-1-get(tg[i]));
        update(tg[i],1);
    }
    printf("%lld\n",ans);
    return  0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值