由于要学习可持久化线段树所以先来补下前缀知识,其实都是很简单的东西
- 首先权值线段树和普通线段树的区别
普通线段树一般维护的是一个区间的最大值,以及一个区间的和等等
故名思意,而权值线段树一般维护的是权值的数量,维护一个区间\([l,r]\)有多少个值
即有多少个值的范围在\([l,r]\)里面
其实我感觉和普通的没什么区别,就是换个名字,高级点
- 而动态开点呢也故名思意
其实就是如果你要查询的范围很大\([1,10^9]\)
但是实际上的点很少,当然可以离散化后处理
但是动态开点就可以避免离散化,因为你实际上没有那么多点,因为有很多点的权值都是0
所以你只要记录下权值不为0的点即可,差不多空间复杂度就是可以压缩到\(nlogn\)的范围内
但是这样你的左儿子和右儿子的权值就不是\(2n\)和\(2n+1\),那么怎么办呢
很简单只要用一个\(lson\)数组和\(rson\)数组记录下来即可
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e7+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n;
int cnt=1;
int tree[maxn],lson[maxn],rson[maxn];
int query(int node,int l,int r,int ql,int qr){
if(node==0) return 0;
//这里没有被建树,显然没有值
if(ql<=l&&r<=qr){
return tree[node];
}
int mid=(l+r)/2,sum=0;
if(mid>=ql) sum+=query(lson[node],l,mid,ql,qr);
if(mid<qr) sum+=query(rson[node],mid+1,r,ql,qr);
return sum;
}
void update(int &node,int l,int r,int pos){
if(node==0) node=++cnt;
// 引用
if(l==r){
tree[node]++;
return ;
}
int mid=(l+r)/2;
if(mid>=pos){
update(lson[node],l,mid,pos);
}else{
update(rson[node],mid+1,r,pos);
}
tree[node]=tree[lson[node]]+tree[rson[node]];
}
signed main(){
scanf("%d",&n);
ll ans=0;
int geng=1;
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
if(x!=1e9){
ans+=query(geng,1,1e9,x+1,1e9);
}
update(geng,1,1e9,x);
}
printf("%lld\n",ans);
return 0;
}