主席树,喵~

Posted RDCのACM奇幻之旅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树,喵~相关的知识,希望对你有一定的参考价值。

稍微总结一下主席树吧

Too Difficult!搞了一天搞出一大堆怎么令人悲伤的辣鸡代码。总之先总结一下吧,以后碰到这种问题直接拿去毒害队友好了。


Lv.1 最基本的操作

  • 区间k大值

  • 区间内有多少个数字小于等于x

query 操作了解一下
int query(int new_k,int old_k,int l,int r,int x) { // cnt <= x
    if(x<l) return 0; // 这个地方比较喜。小心点。
    if(l==r) return sum[new_k]-sum[old_k];
    int mid=(l+r)>>1;
    if(mid<x) return sum[lson[new_k]]-sum[lson[old_k]]+query(rson[new_k],rson[old_k],mid+1,r,x);
    else return query(lson[new_k],lson[old_k],l,mid,x);
}

两道入门题:POJ2104HDU4417

目前对主席树很naive的理解:主席树相当于对每一个前缀都维护一个线段树,然后发现相邻两棵线段树长得好像哎!然后就是资源重复利用了。


既然是维护每一个前缀,所以,我们不仅能拿主席树来施展线性结构,还能施展树状结构!比如说我们可以查询树上两点间路径点权的k小值。

Lv.2 树上路径上点权k小值

栗子:SPOJ-COT

线性结构上

iterval(l,r)=T(r)-T(l-1)

树状结构上

path(u,v) = T(u)+T(v)-T(lca)-T(Parent of lca)


Lv.2 矩形内有多少个点

给出很多个点。Q组询问,每组询问查询一个矩形内有几个点。

按横坐标排序,把纵坐标放到主席树上,然后就相当于区间内有多少个数字小于等于x啦!

CF853C

把细节考虑好!还是很友好的。

#include <iostream>
#include <algorithm>
using namespace std;
const int N=6000000+10;
#define f(x) (1LL*x*(x-1)/2)
typedef long long LL;
int lson[N],rson[N],sum[N],root[N],pid;
int n,q,p[N];
void build(int &k,int l,int r){
    k=++pid;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(lson[k],l,mid);
    build(rson[k],mid+1,r);
}
void change(int old,int &k,int l,int r,int pos,int x) {
    k=++pid;
    lson[k]=lson[old],rson[k]=rson[old],sum[k]=sum[old]+x;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) change(lson[k],lson[k],l,mid,pos,x);
    else change(rson[k],rson[k],mid+1,r,pos,x);
}
int query(int new_k,int old_k,int l,int r,int x) { // cnt <= x
    if(x<l) return 0;
    if(l==r) return sum[new_k]-sum[old_k];
    int mid=(l+r)>>1;
    if(mid<x) return sum[lson[new_k]]-sum[lson[old_k]]+query(rson[new_k],rson[old_k],mid+1,r,x);
    else return query(lson[new_k],lson[old_k],l,mid,x);
}
int count(int x1,int x2,int y1,int y2) { // 
    if(x1>x2||y1>y2) return 0;
    int cnt1 = query(root[x2],root[x1-1],1,n,y1-1);
    int cnt2 = query(root[x2],root[x1-1],1,n,y2);
    return cnt2-cnt1;
} 
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) {
        scanf("%d",&p[i]);
    }
    build(root[0],1,n);
    for(int i=1;i<=n;i++) {
        change(root[i-1],root[i],1,n,p[i],1);
    }
    for(int i=1;i<=q;i++){
        int l,d,r,u;
        scanf("%d%d%d%d",&l,&d,&r,&u);
        int LU = count(1,l-1,u+1,n);
        int LD = count(1,l-1,1,d-1);
        int RU = count(r+1,n,u+1,n);
        int RD = count(r+1,n,1,d-1);

        int L = l-1; int U = n-u; 
        int R = n-r; int D = d-1;
        
        LL A = f(L)+f(R)+f(U)+f(D);
        LL B = f(LU)+f(LD)+f(RU)+f(RD);
        LL ret = 1LL*n*(n-1)/2-(A-B);
        printf("%lld\n", ret);
    }
}

以上,于4/28,mark一下。

之后,待补的坑:

  • BIT套主席树
  • 主席树的区间更新

学数据结构是不可能学数据结构的,这辈子都不可能学数据结构!

以上是关于主席树,喵~的主要内容,如果未能解决你的问题,请参考以下文章

[数据结构] 主席树初识(理论,代码待补)

代码源 Div1 - 108#464. 数数(主席树,区间比k小的数的个数)HDU4417

主席树学习记录

Yangk's 静态主席树-模板

bzoj2809 [ APIO2012 ] -- 主席树

主席树