• VJ Ultra-QuickSort (逆序对)


    原题

    逆序对定义:
    对于一个序列 a,若 i < j 且 a[i] > a[j],则称 a[i] 与 a[j] 构成逆序对

    如何使用树状数组求序列逆序对个数:
    任意给定一个集合 a,如果用 c[val] 保存数值 val 在集合 a 中出现的次数,那么数组 c 在 [l,r] 上的区间和就表示集合 a 在范围 [l,r]内的数有多少个。

    1. 在序列 a 的数值范围上建立树状数组 c[N],初始化全为零。
    2. 倒序扫描给定的序列 a,对于每个数 a[i]:
      1.在树状数组中查询前缀和[1,a[i]-1](我们只需知道在a[i]之前有多少个数,就知道有多少个逆序对),累加到答案中。
      2.执行“单点增加”操作,即把位置 a[i] 上的数加1(相当于c[a[i]]++,a[i]这个数出现次数加一),同时正确维护 c 的前缀和。
    3. ans即为所求
       for(int i=n;i>=1;i--)
    {
         ans+=ask(a[i]-1);//因为查询的是a[i]-1,所以该行代码可与add操作互换位置
         add(a[i],1);
    }
    

    根据以上思路,得到如下代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll n,a[500005],c[500005],maxn;
    ll ask(ll x)
    {
        ll ans=0;
        for(; x; x-=x&-x)
        {
            ans+=c[x];
        }
        return ans;
    }
    void add(ll x,ll y)
    {
        for(; x<=maxn; x+=x&-x)//注意<=maxn
        {
            c[x]+=y;
        }
    }
    int main()
    {
        while(cin>>n,n)
        {
               maxn=0;
            for(ll i=1; i<=n; i++)
            {
                scanf("%lld",&a[i]);
                a[i]+=1;
                maxn=max(maxn,a[i]);//记录最大的数maxn,因为上面提到树状数组是在数值范围上建立的
            }
            ll ans=0;
            for(ll i=n; i; i--)
            {
                add(a[i],1);
                ans+=ask(a[i]-1);
            }
            cout<<ans<<endl;
            memset(c,0,sizeof(c));//注意初始化
        }
        return 0;
    }
    

    测下样例,完美!交一发,RE?
    原来这题a[i]的数据范围特别大,树状数组也无法开到这么大的空间,所以得先对数据离散化处理

    我们可以再开个数组d[N]用来记录a[i],然后将d[N]从小到大排序,再用lower_bound()函数进行查询,得到a[i]的相对位置(数组下标)

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<set>
    using namespace std;
    typedef long long ll;
    ll n,a[500005],c[500005],d[500005],maxn;
    ll ask(ll x)
    {
        ll ans=0;
        for(; x; x-=x&-x)
        {
            ans+=c[x];
        }
        return ans;
    }
    void add(ll x,ll y)
    {
        for(; x<=n; x+=x&-x)//注意这里树状数组的数值范围是n
        {
            c[x]+=y;
        }
    }
    int main()
    {
        while(cin>>n,n)
        {
            for(ll i=1; i<=n; i++)
            {
                scanf("%lld",&a[i]);
                d[i]=a[i];
            }
            ll ans=0;
            sort(d+1,d+n+1);
            for(ll i=n; i>=1; i--)
            {
                int x=lower_bound(d+1,d+n+1,a[i])-d;
                add(x,1);
                ans+=ask(x-1);
            }
            cout<<ans<<endl;
            memset(c,0,sizeof(c));
        }
        return 0;
    }
    

    当然也可以开个结构体,然后排序,总之离散化的方法有很多。还有数据范围要开到ll

    戒骄戒躁,百炼成钢!
  • 相关阅读:
    常用算法之选择排序
    常用算法之插入排序
    常用算法之冒泡排序
    Python hashlib模块 (主要记录md5加密)
    Django Model
    CSS实现table td中文字的省略与显示
    JS读取文件,Javascript之文件操作 (IE)
    ie6789和其他浏览器之间的鼠标左、中、右键的event.button不一致的办法
    兼容和样式
    kindeditor的docs
  • 原文地址:https://www.cnblogs.com/Pecoz/p/12404346.html
Copyright © 2020-2023  润新知