树状数组
题意
- 给出一个数组,求每个位置上的值,在其右边有多少个比它小的值。
注意
- 数组中没有重复元素。我的原版本中有个小bug,有重复元素的时候,编号应该是
A[E[i].pos] = A[E[i-1].pos];
- 树状数组从1开始编号
思路
- 因为不知道数值的范围,所以先将数组中的元素离散化。即将其映射到1到1e5的范围内,
A
数组是原下标映射成的新下标。比如有数组{ 24, 35, 12, 1, 56, 23 },
最后一个元素23,下标是i = 5
,离散化后下标是A[5] = 3
。 - 直接套树状数组的模板
原版本
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e5+7;
#define low(x) ((x)&(-x))
int n, x;
struct Node{
int val; //原始值
int pos; //原始序号
bool operator <(const Node& a) const{
return val < a.val;
}
}E[N];
int A[N], c[N], res[N];
void update(int x, int v){
while(x <= n){
c[x] += v;
x += low(x);
}
}
int sum(int x){
int sumx = 0;
while(x){
sumx += c[x];
x -= low(x);
}
return sumx;
}
int main(){
cin>>n;
for(int i = 0; i < n; i++){
cin>>E[i].val;
E[i].pos = i; //原始序号
}
sort(E, E+n); //按值排序
for(int i = 0; i < n; i++){
if(!i || E[i-1].val != E[i].val){
A[E[i].pos] = i + 1; //树状数组必须从1开始 只是相对顺序
}else{
A[E[i].pos] = E[i-1].pos; //如果相等说明相对大小一致 直接继承前面的大小
}
}
//更新获得树状数组
//使用hash获得比A[i]小的个数
for(int i = n - 1 ; i >= 0; i--){
update(A[i], 1); //原始下标为n-1的数值的相对大小为A[i]
res[i] = sum(A[i] - 1); //找到hash[A[i]-1] + hash[A[i]-2]+ ... + hash[2] + hash[1] 前缀和
}
for(int i = 0 ; i < n; i++){
if(i) cout<<" ";
cout<<res[i];
}
return 0;
}
新版
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e5+7;
#define low(x) ((x)&(-x))
int n, x;
struct Node{
int val; //原始值
int pos; //原始序号
bool operator <(const Node& a) const{
return val < a.val;
}
}E[N];
int A[N], c[N], res[N];
void update(int x, int v){
while(x <= n){
c[x] += v;
x += low(x);
}
}
int sum(int x){
int sumx = 0;
while(x){
sumx += c[x];
x -= low(x);
}
return sumx;
}
int main(){
cin>>n;
for(int i = 0; i < n; i++){
cin>>E[i].val;
E[i].pos = i; //原始序号
}
sort(E, E+n); //按值排序
for(int i = 0; i < n; i++){
if(!i || E[i].val == E[i-1].val)
A[E[i].pos] = i + 1; //树状数组必须从1开始 只是相对顺序
else{
A[E[i].pos] = A[E[i-1].pos];
}
}
//更新获得树状数组
//使用hash获得比A[i]小的个数
for(int i = n - 1 ; i >= 0; i--){
update(A[i], 1); //原始下标为n-1的数值的相对大小为A[i]
res[i] = sum(A[i] - 1); //找到hash[A[i]-1] + hash[A[i]-2]+ ... + hash[2] + hash[1] 前缀和
}
for(int i = 0 ; i < n; i++){
if(i) cout<<" ";
cout<<res[i];
}
return 0;
}