• 权值线段树2(求逆序对)


    题目链接:逆序对1逆序对2

    都是板子,一摸一样,双倍积分

    一.逆序对是什么?

    对于给定的一段正整数序列,逆序对就是序列中ai>aj且i< j的有序对

    通俗来讲,就是有一列数,如果有这样两个数m,n满足

    • m在n前面
    • m比n大

    那么m,n就是一对逆序对,现在我们要求的就是这种逆序对个数。

    二.权值线段树实现方法

    我们能够想到这样一种方法:

    1. 读入一个数,查找之前读入的数中比它大的数的个数,累加答案
    2. 把它丢进某种数据结构中,方便之后查找

    而这“某种数据结构”不就是权值线段树吗?

    查询:
    对于查询数x与此时左子树右边界mid( mid=(l+r)>>1 )
    若x<=mid,则答案=左子数比x大的数的个数+右子树的数的个数
    若x>mid,则答案=右子树比x大的数的个数
    递归处理即可
    一定记住权值线段树下标维护的是值域!!

    插入:
    与先前相同

    但是

    a[i]<=INT_MAX
    权值线段树会爆!!!

    怎么办?
    n好像十分友善:n\(<=\)\(5*10^{5}\)
    离散化
    离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。
    我们发现并不需要记录每一个数的具体数值,只要记录他们之间的大小关系即可。
    具体看代码。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    long long a[500005],ans,n;
    struct MM{
        long long ii,num;
    }b[500005];
    struct MMM{
        long long l,r,sum;
    }tree[2000005];
    bool cmp(MM x,MM y)
    {
        return x.num<y.num;
    }
    void build(long long root,long long L,long long R)
    {
        tree[root].l=L;
        tree[root].r=R;
        if(tree[root].l==tree[root].r)
            return;
        long long mid=(L+R)>>1;
        build(root<<1,L,mid);
        build(root<<1|1,mid+1,R);
        return;
    }
    void updata(long long root,long long v)
    {
        if(tree[root].l==tree[root].r)
        {
            tree[root].sum++;
            return;
        }
        long long mid=(tree[root].l+tree[root].r)>>1;
        if(v<=mid) updata(root<<1,v);
        else updata(root<<1|1,v);
        tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
        return;
    }
    long long query(long long root,long long x)
    {
        if(tree[root].l==tree[root].r) return tree[root].sum;
        long long mid=(tree[root].l+tree[root].r)>>1;
        if(x<=mid) return query(root<<1,x)+tree[root<<1|1].sum;
        return query(root<<1|1,x);
    }
    int main()
    {
        cin>>n;
        for(long long i=1;i<=n;i++)//这里离散化开始
        {
            cin>>a[i];
            b[i].num=a[i];
            b[i].ii=i;
        }
        sort(b+1,b+n+1,cmp);
        for(long long i=1,j=0;i<=n;i++)
        {
            if(b[i].num!=b[i-1].num||i==1) j++;
            a[b[i].ii]=j;
        }//这里离散化结束,可以看看或者自己算算加深理解
        build(1,1,n);
        for(long long i=1;i<=n;i++)
        {
            ans+=query(1,a[i]+1);//这里有个小细节:为什么要加一呢?因为权值线段树算的是大于等于x的数的个数,而我们要求的是严格大于x的数的个数,所以加一(具体可以看看查询函数或手算试试)
            updata(1,a[i]);
        }
        cout<<ans;
        return 0;
    }
    
    NO PAIN NO GAIN
  • 相关阅读:
    Android获取两条线之间的夹角度数
    Android字体度量(FontMetrics)
    Android下如何计算要显示的字符串所占的宽度和高度
    Android 颜色渲染PorterDuff及Xfermode详解
    从输入URL到页面加载的全过程
    滚动优化
    常用的前端相关chrome插件
    DNS预解析prefetch
    资源预加载preload和资源预读取prefetch简明学习
    使用chrome开发者工具中的performance面板解决性能瓶颈
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/9595230.html
Copyright © 2020-2023  润新知