树状数组求逆序对(逆序数)

逆序数(也叫逆序对)
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

【1】无重复的数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAX 500000
typedef long long ll;
ll c[MAX];
ll n;
struct record
{
    ll val, pos;//记录数值 和 位置
}num[MAX];
bool cmp(record a,record b)
{
    return a.val > b.val;
}
ll lowbit(ll x)
{
    return x&(-x);
}
ll sum(ll x)//求和
{
    ll s = 0;
    while(x > 0)
    {
        s += c[x];
        x -= lowbit(x);
    }
    return s;
}
void update(ll x)//更新
{
    while(x <= n)
    {
        c[x] += 1;
        x += lowbit(x);
    }
}
int main()
{
    ll i, j;
    ll x,y;
    ll ans;//统计总数目
    while(~scanf("%lld%lld%lld", &n,&x,&y))
    {
        memset(c, 0, sizeof(c));//初始化
        for(i = 0;i < n; i++)
        {
            scanf("%lld", &num[i].val);
            num[i].pos = i + 1;
        }
        sort(num, num+n, cmp);
        ans = 0;
        for(i = 0;i < n; ++i)//每一次插入当前最大值
        {
            update(num[i].pos);
            ans += sum(num[i].pos-1);//统计前面有多少个数
        }
        ll anss=ans*min(x,y);
        printf("%lld\n",anss);
    }


}

【2】有重复的数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAX 500000
typedef long long ll;
ll c[MAX];
ll n;
struct record
{
    ll val, pos;//记录数值 和 位置
}num[MAX];
bool cmp(record a, record b)
{
    if(a.val != b.val)
    return a.val > b.val;
    else
    return a.pos > b.pos;
}

ll lowbit(ll x)
{
    return x&(-x);
}
ll sum(ll x)//求和
{
    ll s = 0;
    while(x > 0)
    {
        s += c[x];
        x -= lowbit(x);
    }
    return s;
}
void update(ll x)//更新
{
    while(x <= n)
    {
        c[x] += 1;
        x += lowbit(x);
    }
}
int main()
{
    ll i, j;
    ll x,y;
    ll ans;//统计总数目
    while(~scanf("%lld%lld%lld", &n,&x,&y))
    {
        memset(c, 0, sizeof(c));//初始化
        for(i = 0;i < n; i++)
        {
            scanf("%lld", &num[i].val);
            num[i].pos = i + 1;
        }
        sort(num, num+n, cmp);
        ans = 0;
        for(i = 0;i < n; ++i)//每一次插入当前最大值
        {
            update(num[i].pos);
            ans += sum(num[i].pos-1);//统计前面有多少个数
        }
        ll anss=ans*min(x,y);
        printf("%lld\n",anss);
    }


}

【3】树状数组求逆序数(离散化)当maxn较大时,直接开数组显然是不行了,这是的解决办法就是离散化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll c[100005],s[100005],a[100005],b[100005];
ll n;
ll x,y;
int lowbit(int x)
{
    return x&(-x);
}
void update(ll i,int x)
{
    while(i<=n)
    {
        c[i]+=x;
        i+=lowbit(i);
    }
}
ll Sum(ll x)
{
    ll sum=0;
    while(x>0)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    while(~scanf("%lld%lld%lld",&n,&x,&y))
    {
        memset(c,0,sizeof(c));
        ll ans=0;
        int m;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            a[i]+=1000000000;
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int len=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;i++)
        {
            a[i]=lower_bound(b+1,b+len+1,a[i])-b;
            update(a[i],1);
            ans+=(ll)i-Sum(a[i]);
        }
        ll ma=min(x,y);
        printf("%lld\n",ans*ma);
    }
}

猜你喜欢

转载自blog.csdn.net/wuxiaowu547/article/details/81211599