牛客练习赛7 - E(树状数组+离散化)

链接:


题目描述

珂朵莉给了你一个序列,有 个子区间,求出她们各自的逆序对个数,然后加起来输出

输入描述:

 
   

第一行一个数 n 表示这个序列 a 的长度

之后一行 n 个数,第i个数表示ai

输出描述:

输出一行一个数表示答案
示例1

输入

10
1 10 8 5 6 2 3 9 4 7

输出

270


题解:假如a[i]和a[j]是一组逆序对,则包含它们的子区间个数为i * (n - j + 1)。使用树状数组求解,先离散化,再每次加入当前数的位置,这样每次询问求的就是位置和,即如果a1,a3,a5都与a7成逆序对,那么询问sum(n) - sum(a[7])的话可以直接求得1 + 3 + 5的值。


#include 
using namespace std;

const long long mod = 1e18;
const int maxn = 1e6 + 10;
int n;
int a[maxn], b[maxn];
long long bit[maxn];

long long sum(int i)
{
    long long res = 0;
    while(i > 0){
        res += bit[i];
        i -= i & -i;
    }
    return res;
}

void add(int i, int x)
{
    while(i <= n){
        bit[i] += x;
        i += i & -i;
    }
}

int main()
{
    scanf("%d", &n);
    int cnt = 0;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        b[cnt++] = a[i];
    }
    
    //离散化
    sort(b, b + cnt);
    cnt = unique(b, b + cnt) - b;
    for(int i = 1; i <= n; i++) a[i] = lower_bound(b, b + cnt, a[i]) - b + 1;

    long long ans = 0, lans = 0;
    for(int i = 1; i <= n; i++){
        ans += (sum(n) - sum(a[i])) * (n - i + 1);      //求(西格玛i)*(n-i+1);
        if(ans >= mod) lans += ans / mod; ans %= mod;   //爆long long 的计数技巧
        add(a[i], i);
    }

    if(lans) printf("%lld%018lld\n", lans, ans);    //爆long long 的计数技巧
    else printf("%lld\n", ans);

    return 0;
}


你可能感兴趣的:(数据结构)