权值线段树与动态开点

由于要学习可持久化线段树所以先来补下前缀知识,其实都是很简单的东西

  • 首先权值线段树和普通线段树的区别

普通线段树一般维护的是一个区间的最大值,以及一个区间的和等等

故名思意,而权值线段树一般维护的是权值的数量,维护一个区间([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
",ans);
    return 0;
}


卷也卷不过,躺又躺不平
原文地址:https://www.cnblogs.com/hunxuewangzi/p/14640641.html