原题链接:https://www.luogu.com.cn/problem/P1908
题意解读:求逆序对,前面介绍过归并排序的做法,参考:https://www.cnblogs.com/jcwy/p/184077,这里介绍树状数组的做法。
解题思路:
设数组a[n]里的整数只包括1~n,显然对于此题,可以通过离散化得到这样的数组。
要计算逆序对,就是要计算对于每一个数,位置靠后且值较小的数有多少个,累加起来即可。
如何统计比每一个数位置靠后且值较小的数的个数?
设b[i]表示值为i的数出现的个数,
可以从后往前遍历a中每一个数,对于每一个遍历到的数a[i],计算b[1] ~ b[a[i] - 1]之和,累加到ans,
然后把a[i]的个数更新b[a[i]]++,如此变可以统计所有的逆序对数量。
问题转化为如何快速计算b的前缀和,已经修改b的值,就可以借助树状数组!
设树状数组tr[N]是b的树状数组,通过sum(a[i] - 1)来计算b[1] ~ b[a[i] - 1]之和,通过add(a[i], 1)来实现b[a[i]]++。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 500005;
int n;
int a[N], b[N], c[N], cnt, tr[N];
map<int, int> h;
LL ans;
int lowbit(int x)
{
return x & -x;
}
void add(int x, int val)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += val;
}
int sum(int x)
{
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
//去重,离散化
memcpy(b, a, sizeof(a));
sort(b + 1, b + n + 1);
for(int i = 1; i <= n; i++)
{
if(b[i] != b[i - 1]) c[++cnt] = b[i], h[c[cnt]] = cnt;
}
for(int i = 1; i <= n; i++) a[i] = h[a[i]];
//逆序对统计
for(int i = n; i >= 1; i--)
{
ans += sum(a[i] - 1);
add(a[i], 1);
}
cout << ans;
return 0;
}
标签:树状,int,洛谷题,P1908,add,数组,逆序 From: https://www.cnblogs.com/jcwy/p/18552254