树状数组+ 离散化 求逆序数

来源:互联网 发布:网络搜索不到打印机 编辑:程序博客网 时间:2024/05/24 06:39

我们先来看一道题。
poj 2299 http://poj.org/problem?id=2299
这道算是 树状数组 + 离散化 求逆序数的基础。

我们先来了解以下离散化的作用:解决数据范围过大。无法用自身作为数组的下表存放。所以,离散化能够帮助我们有效的解决:当数据只与它们之间的相对大小有关,而与具体是多少无关的问题。

在离散化中用到unique函数:
unique的作用是“去掉”容器中相邻元素的重复元素,它实质上是一个伪去除,它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址。

//#include<bits/stdc++.h>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 501000#define ll long longint a[N], s[N], sum[N];int n;void update(int x, int y){    while (x <= n){        sum[x] += y;        x += x & -x;    }}ll read(ll x){    ll ans = 0;    while (x){        ans += sum[x];        x -= x & -x;    }    return ans;}int main (){     while (scanf("%d",&n)!=EOF && n){        memset(sum,0,sizeof(sum));        memset(s,0,sizeof(s));        for (int i = 1; i <= n; i++)            scanf("%d",&a[i]), s[i] = a[i];        sort(s+1,s+n+1);        ll Ans = 0;        int k = unique(s+1, s+n+1) - s;        for (int i = 1; i <= n; ++i) a[i] = lower_bound(s+1,s+n+1,a[i]) - s ;        for (int i = 1; i <= n; ++i){            ll temp = read(a[i]);            Ans += i -temp - 1;            update(a[i],1);        }        printf("%lld\n",Ans);    }     return 0;}

这道题 完全可以作为 树状数组+ 离散化 求逆序数 的模板。

接下来放一道最近最到的难题。 也正因为如此才会再一次地看下树状数组。

牛客练习赛7 E 珂朵莉的数列

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
珂朵莉给了你一个序列,有个子区间,求出她们各自的逆序对个数,然后加起来输出
输入描述:
第一行一个数 n 表示这个序列 a 的长度

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

输出描述:
输出一行一个数表示答案
示例1
输入
10
1 10 8 5 6 2 3 9 4 7
输出
270
示例2
输入
20
6 0 4 5 8 8 0 6 6 1 0 4 6 6 0 0 7 2 0 5
输出
3481
备注:
对于100%的数据,n <=
1000000 ,0 <= 序列中每个数 <= 1000000000

官方题解:
10分做法:puts( “0” );
30分做法:直接暴力
60分做法:优化暴力
70分做法:
考虑计算贡献
如果有i < j , a[i] > a[j]的点对( i , j )
则会在1 <= l <= i , j <= r <= n的子区间[l,r]中出现
则对答案的贡献是i * ( n - j + 1 )
所以用树状数组维护或者分治(归并排序)即可
做法和求普通逆序对差不多
80->100分做法:
发现答案是O( n^4 )级别
冷静分析出题人会不会卡爆long long
发现随机数据答案是O( n^4 )级别
放弃吧,还是写高精度吧
根据常数不等,80分->100分

卡到80% 估计很绝望233,高精度真的太秀了,我的龟龟。
但是这道题可以不用高精度写,在大佬那儿学到了一个的小技巧(●ˇ∀ˇ●)。
如何处理爆long long!

#include<bits/stdc++.h>using namespace std;#define ll long long#define N 1100000ll a[N],b[N],sum[N];ll n;void update(int x, int y){    while (x <= n){        sum[x] += y;        x += x & -x;    }}ll read (ll x){    ll ans = 0;    while (x){        ans += sum[x];        x -= x &-x;    }    return ans;}int main (){    scanf("%lld",&n);    for (int i = 1; i <= n; i++) scanf("%lld",&a[i]), b[i] = a[i];    sort(b+1,b+1+n);    int k = unique(b+1,b+1+n) - b;    ll ans1 = 0, ans2 = 0;    ll mx = 1e18;    for (int i = 1; i <= n; i++){        a[i] = lower_bound(b+1,b+1+n,a[i]) - b;        ans1 += (ll)(n-i+1) * (read(n) - read(a[i]));        if (ans1 >= mx) ans2+= ans1/mx, ans1 %= mx;        update(a[i],i);    }    if (ans2) printf("%lld%018lld\n",ans2,ans1);    else printf("%lld\n",ans1);    return 0;}