Luogu p1908逆序对模板(线段树版)

博客介绍了用线段树求解逆序对问题的方法。逆序对是求每个数前面比它大的数的数量,可考虑权值线段树。因数据范围大,需预处理,如排序、去重、编号等。最后可构造线段树求解,还给出了代码思路。

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

luogu里也有我的这篇题解(asd_a)
大佬应该都用的是树状数组和归并,我就讲一讲线段树的方法吧!
逆序对就是求每一个数前面有多少个比它大,
则考虑权值线段树。
什么是权值线段树? 就是以数的大小作区间的线段树。
权值线段树可干啥?整体第k大……(其实就是在区间比大小)
我们就只用查询线段树中有多少个比他大,然后再加入线段树
答案之和就是逆序对个数(是不是很神奇啊)
然而,此题数据范围太大,所以需要一个小小的预处理操作:
例:2 8 0 3 2,
我们可以排个序(要保留原版):0 2 2 3 8
去重:0 2 3 8,
编号为:1,2,3,4。再对应回去:2 4 1 3 2
(要开个结构体,记录原位置)
一系列骚操作得: 1<=AiA_iAi<=n
好了,现在可以构造了。(开始值都为0)
coding:
(代码有点丑,望大佬谅解 orz)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int rd()
{
    int x=0,flag=1;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*flag;
}
inline LL rdl()
{
    LL x=0,flag=1;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*flag;
}//读入优化
const int N=5e5+50;
const int TR=N<<2;
struct tree{
    int l,r,k;
}t[TR];
inline int ls(int x)//左儿子
{
    return x<<1;
}
inline int rs(int x)//右儿子
{
    return x<<1|1;
}
struct no{
    int x,i;
}cop[N];
bool comp(no x,no y)
{
    return x.x<y.x;
}
inline void build(int now,int l,int r)//建树
{
    t[now].l=l;
    t[now].r=r;
    t[now].k=0;
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    build(ls(now),l,mid);
    build(rs(now),mid+1,r);
    return ;
}
inline void up(int now)//回溯
{
    t[now].k=t[rs(now)].k+t[ls(now)].k;
}
inline LL src(int now,int x)//搜索
{
    int l=t[now].l,r=t[now].r;
    LL sum=0;
    int mid=(l+r)>>1;
    if(l==r)
        return (LL)t[now].k;
    if(x<=mid) sum+=(LL)t[rs(now)].k+src(ls(now),x);//若在左儿子中,右儿子一定比它大
    else sum+=src(rs(now),x);//搜索右儿子
    return sum;
}
inline void upate(int now,int x)//加入
{
    int l=t[now].l,r=t[now].r;
    int mid=(l+r)>>1;
    if(l==r)
    {
        t[now].k++;//统计个数
        return ;
    }
    if(x<=mid) upate(ls(now),x);
    else upate(rs(now),x);
    up(now);
}
int n;
LL a[N],k[N],tot=0,ans=0;//开long long防爆
int main()
{
    n=rd();
    for(int i=1;i<=n;i++) a[i]=rdl(),cop[i].x=a[i],cop[i].i=i;
    sort(cop+1,cop+n+1,comp);
    for(int i=1;i<=n;i++)
    {
        if(cop[i].x!=cop[i-1].x || i==1)
            k[++tot]=cop[i].x;
        a[cop[i].i]=tot;
    }//预处理
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        ans+=src(1,a[i]+1);//大于a的个数=大于等于a+1的个数
        upate(1,a[i]);//更新
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值