主席树模版,区间第K大(静态不带修改,带修改)简单理解。

蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒻 的博客,有错正常不过了,见谅见谅。

主席树模版,静态区间第K大

主席树用于解决类似这样的问题。  有一个数组a[],数组大小一般为1e5,有Q个询问,询问个数也是1e5。

每一个询问给出三个整数  l,r,k,问从区间 l 到 r 第 k 大的值是多少。

需要的前置知识:权值线段树。

权值线段树

用于解决类似这样的问题。  有一个数组a[],数组大小一般为1e5,含增、删、改操作,最后询问整个区间的第 K 大的值是多少。

权值线段树:简的来说,就是用下标代替值,然后维护区间和,即得到了区间包含的数字的个数。

例如:数组 a[] = {1,2,4,5}

就得到

sum[8] = 1,sum[9] = 1,sum[4] = 2,sum[2] = 2.

sum[6] = 1, sum[7]  = 1, sum[3] = 2

sum[1] = 4.

那么对于区间[1 , 5] 的第 k 大就可以这样来查询;

如果  sum[右儿子] >= k , 递归向右儿子,表示第 k 大一定在右儿子的子树上,继续查询第 k 大。

else  递归向左儿子,表示右儿子占有前 sum[右儿子] 大,那么第 k 大一定在左儿子的子树上,查询第 k - sum[右儿子] 大

那么遇到数值范围很大怎么办呢? 离散化一下,就可以解决了。

权值线段树: bzoj 1503,可以用权值线段树去做一做。

 

以下所带数组的含义:K[] 表示原数组,X[] 表示复制K[] 然后用来离散化的数组,lson[i] , rson[i] 表示节点 i 的左儿子 与 右儿子。

C[] 代表一个结点的值,T[i] 表示第 i 颗线段树的根节点

静态主席树

简单可以理解为就是 n 颗权值线段树。第 i 棵权值线段树是根据区间 [1,i] 按值建立的。对于每一棵线段树我们记录它对应的区间每个数出现的次数,一般都需要对所有的数离散化。

那么对于区间 [l , r] 的信息,就可以用 第 r 颗权值线段树 - 第 l 颗权值线段树得到。

如果每一颗线段树都建立完整,那么肯定MLE。

对于第 i - 1 颗树 和 第 i 颗树,不同的点只有 log N 个,采用动态开点,每一次更新都新建 log N 个节点,然后和上一颗线段树连上,就完美解决了。

动态主席树

动态主席树相对于静态主席树  可以修改原数组的值,最后询问第k小。

新增数组含义:S[] 表示树状数组,S[i] 即 第 i 棵线段树的根节点,use[] 表示树状数组查询会经过的线段树的根结点

大致原理:对于修改,很显然  修改值 K[i] 影响的是  第 i 颗 前缀线段树 到 第 n 颗前缀线段树,所以使用树状数组来维护(每一个下标都代表一颗线段树,共 n 棵),起始时就是 n 棵构建好的空树,查询时就是  分别查询  树状数组 和 静态主席树 ,取和。

对于每一次修改,我们都先去除原影响,在添加新影响。

由于会带修改,所以离散化数组X[],需要包含修改后的数值,也就是得把修改后的值一并加入离散化数组(所以建立数组的时候要包含上询问的大小)。

那么动态主席树的大致模版步骤即:输入,数组K,并复制至数组X,然后输入询问,保存所有修改后的值至数组X,然后离散化,建立静态主席树,对树状数组初始化,最后处理所有询问。若是修改,则利用树状数组 先消除原影响,在添加新影响,是查询 就直接query即可。

这里是我学习时遇到的问题:

  • 树状数组是怎么维护修改的:这里需要理解,树状数组起始时就 n 棵,而不是 m 棵。因为修改的时候影响是 第 i 到 n 颗前缀树,也就是每一棵树,维护一个对应位置的修改。
  • 树状数组查询时为什么就是简单的用 C[lson[use[x]]] , 其实这个和为什么query的都是用左儿子 一样的道理。要同步。

静态主席树第 k 小模版代码:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define fi first
#define se second
#define ptch putchar
#define CLR(a) while(!(a).empty()) a.pop()

using namespace std;

const int maxn = 2e5 + 10;
const int M = maxn * 40;
int T[maxn];///T[i]表示第i颗线段树的顶结点
int lson[M],rson[M],C[M];///c[i]就代表第i个点的权值,即上述权值线段树的sum
int X[maxn],K[maxn];     /// lson[i], rson[i] 表示第 i 个点的左儿子,右儿子是谁
int tot = 0,en;          /// tot 用于动态开点

int getId(int x){
    return lower_bound(X + 1,X + 1 + en,x) - X;
}

void build(int &i,int l,int r){
    i = ++ tot;
    C[i] = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(lson[i],l,mid);
    build(rson[i],mid + 1,r);
}

void update(int l,int r,int &i,int last,int pos){
    C[++ tot] = C[last];
    lson[tot] = lson[last];
    rson[tot] = rson[last];
    i = tot;
    ++ C[i];
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(pos <= mid)
        update(l,mid,lson[i],lson[last],pos);
    else
        update(mid + 1,r,rson[i],rson[last],pos);
}

int query(int l,int r,int lrt,int rrt,int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int tmp = C[lson[rrt]] - C[lson[lrt]];
    if(tmp >= k)
        return query(l,mid,lson[lrt],lson[rrt],k);
    return query(mid + 1,r,rson[lrt],rson[rrt],k - tmp);
}

int main() {
    int n,m; scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++ i){
        scanf("%d",&K[i]);
        X[i] = K[i];
    }
    sort(X + 1,X + 1 + n);
    en = unique(X + 1,X + 1 + n) - X - 1;
    build(T[0],1,n);
    for(int i = 1;i <= n;++ i)
        update(1,n,T[i],T[i - 1],getId(K[i]));
    while(m --){
        int l,r,k; scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",X[query(1,n,T[l - 1],T[r],k)]);
    }
    return 0;
}

动态主席树第 k 小模版:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs i << 1 + 1
#define INT(t) int t; scanf("%d",&t)

using namespace std;

const int maxn = 6e4 + 10;
const int M = maxn * 40;
int T[maxn],S[maxn],use[maxn];
int lson[M],rson[M],C[M];
int K[maxn],X[maxn];
struct xx{
    int l,r,k;
    int x,val;
    bool f;
}Q[maxn / 6];
int tot,cnt,en;
int n,m;

int build(int l,int r){
    int rt = tot ++;
    C[rt] = 0;
    if(l != r){
        int mid = (l + r) >> 1;
        lson[rt] = build(l,mid);
        rson[rt] = build(mid + 1,r);
    }
    return rt;
}

int update(int rt,int pos,int val){
    int newrt = tot ++,tmp = newrt;
    C[newrt] = C[rt] + val;
    int l = 1,r = en;
    while(l < r){
        int mid = (l + r) >> 1;
        if(pos <= mid){
            r = mid;
            lson[newrt] = tot ++;
            rson[newrt] = rson[rt];
            newrt = lson[newrt];
            rt = lson[rt];
        }
        else {
            l = mid + 1;
            rson[newrt] = tot ++;
            lson[newrt] = lson[rt];
            newrt = rson[newrt];
            rt = rson[rt];
        }
        C[newrt] = C[rt] + val;
    }
    return tmp;
}

#define lowbit(x) ((x) & (-x))
void add(int x,int pos,int val){
    while(x <= n){
        S[x] = update(S[x],pos,val);
        x += lowbit(x);
    }
}

int getSum(int x){
    int res = 0;
    while(x){
        res += C[lson[use[x]]];
        x -= lowbit(x);
    }
    return res;
}

int query(int lr,int rr,int k){
    int lrt = T[lr - 1];
    int rrt = T[rr];
    for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = S[i];
    for(int i = rr;i ;i -= lowbit(i)) use[i] = S[i];
    int l = 1,r = en;
    while(l < r){
        int tmp = getSum(rr) - getSum(lr - 1) + C[lson[rrt]] - C[lson[lrt]];
        int mid = (l + r) >> 1;
        if(tmp >= k){
            r = mid;
            for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = lson[use[i]];
            for(int i = rr;i ;i -= lowbit(i)) use[i] = lson[use[i]];
            lrt = lson[lrt]; rrt = lson[rrt];
        }
        else {
            l = mid + 1;
            k -= tmp;
            for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = rson[use[i]];
            for(int i = rr;i ;i -= lowbit(i)) use[i] = rson[use[i]];
            lrt = rson[lrt]; rrt = rson[rrt];
        }
    }
    return l;
}

int getId(int x){
    return lower_bound(X + 1,X + 1 + en,x) - X;
}

int main() {
    int t; scanf("%d",&t);
    while(t --){
        tot = cnt = 0;
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;++ i){
            scanf("%d",&K[i]);
            X[++ cnt] = K[i];
        }
        char op;
        for(int i = 1;i <= m;++ i){
            scanf(" %c",&op);
            if(op == 'Q'){
                int l,r,k; scanf("%d%d%d",&l,&r,&k);
                Q[i].l = l; Q[i].r = r; Q[i].k = k;
                Q[i].f = 0;
            }
            else {
                int x,val; scanf("%d%d",&x,&val);
                Q[i].x = x; Q[i].val = val;
                X[++ cnt] = val;
                Q[i].f = 1;
            }
        }
        sort(X + 1,X + 1 + cnt);
        en = unique(X + 1,X + 1 + cnt) - X - 1;
        T[0] = build(1,en);
        for(int i = 1;i <= n;++ i){
            T[i] = update(T[i - 1],getId(K[i]),1);
        }
        for(int i = 1;i <= n;++ i) S[i] = T[0];
        for(int i = 1;i <= m;++ i){
            if(Q[i].f == 1){
                add(Q[i].x,getId(K[Q[i].x]),-1);
                add(Q[i].x,getId(Q[i].val),1);
                K[Q[i].x] = Q[i].val;
            }
            else {
//            cout << " 1" << endl;
                printf("%d\n",X[query(Q[i].l,Q[i].r,Q[i].k)]);
            }
        }
    }
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值