• 数据结构小结+模板


    $$OI中的数据结构$$

    $$By;TYQ$$

    线性结构

    大部略

    单调栈

    栈 , 但是push的时候要弹出所有比他小/大的(多用于优化Dp)

    单调队列

    队列 , 同单调栈

    树状结构

    树状数组

    核心:lowbit(x) = (x) & (-x)

    ...其实lowbit(x) = 2^x的最低非0位

    PION8012初赛中考了...但只涉及正数...

    • 为什么lowbit(x) = (x) & (-x)

    考虑x>0时-x等于多少:-x在二进制中的意义为x所有位取反后+1 , 那么他的第一个非0位以前的都是0 , &后结果为0 , 在第一个非0位时-x发生进位使哪位为1 , 那么这位为1 , 再以后呢?为0 , 所以&也为0

    • 树状数组每个节点的值

    (C_{i} = sum_{j=i}^{j!=0 , j=lowbit(j)} C_{j})

    • 树状数组怎么求和 :

    我们为什么要设计前面的(C_{i})为这个奇怪的数?

    答案揭晓!为了查询!当查询时我们只需要遍历查询数的每个lowbit , 将值加上(C_{x})

    查询时间复杂度为(O(logN))

    那么这为什么对呢?因为:

    .

    .

    .

    画出这颗树发现的确是对的QAQ其实是我不会证

    关于代码:

    void modify(int x , int y)/*modify a[x] to a[x]+y*/{for(int i=x;i<=n;C[i]+=y,i+=(i)&(-i)) ; }
    int query(int x)/*query a[1]+...+a[x]*/{int ret = 0 ;for(int i=x;i;i-=(i)&(-i))ret+=C[i] ; return ret ;}
    

    线段树

    其实一开始你觉得比较难 , 但其实很基础

    线段树每次将区间分成两个小区间 , 到底层递归 ;

    用指针写伪代码就是

    Query()
        int sum = 0 ;
        if(!this->ls) sum+=this->ls.Query()
        if(!this->rs)sum+=this->rs.Query()
        return sum ;
    
    //什么?你问我为什么要写指针?代码短!
    

    但是你代码肯定不能这么写...线段树维护的不只和 , 需要zici区间查询 :

    关于区间查询

    Query(i,j,ni,nj,p)
        if(i<=ni && j<=nj) return Value[p] ;
        int m = (s + t) >> 2, sum = 0;
        if (l <= m) sum += Query(l, r, s, m, p * 2);
        if (r > m) sum += Query(l, r, m + 1, t, p * 2 + 1);
        return sum;
    

    pushdown

    pushdown(p,m,s,t)
        d[p * 2] += b[p] * (m - s + 1), d[p * 2 + 1] += b[p] * (t - m),b[p * 2] += b[p], b[p * 2 + 1] += b[p];
    

    区间修改:

    每次执行单点修改并给节点打上LazyTag , 需要时下放

    板子

    #include<iostream>
    #include<cstdio>
    #define ll		long long
    #define MAXN 100000
    int p , arr[MAXN+5];
    using namespace std ;
    struct node{
        ll v,mul,add;
    }tr[4*MAXN+5];
    void build(int root,int l,int r){
        tr[root].mul=1,tr[root].add=0;
        if(l==r) tr[root].v=arr[l];
        else {
            int m=(l+r)/2; 
            build(root*2,l,m) ; 
            build(root*2+1,m+1,r) ;
            tr[root].v=tr[root*2].v+tr[root*2+1].v ;
        }
        tr[root].v%=p ;
    }
    void pushdown(int root,int l,int r){
        int mid=(l+r)/2 ;
        tr[root*2].v=(tr[root*2].v*tr[root].mul+tr[root].add*(mid-l+1))%p,
        tr[root*2+1].v=(tr[root*2+1].v*tr[root].mul+tr[root].add*(r-mid))%p,
        tr[root*2].mul=(tr[root*2].mul*tr[root].mul)%p,
        tr[root*2+1].mul=(tr[root*2+1].mul*tr[root].mul)%p,
        tr[root*2].add=(tr[root*2].add*tr[root].mul+tr[root].add)%p,
        tr[root*2+1].add=(tr[root*2+1].add*tr[root].mul+tr[root].add)%p,
        tr[root].mul=1,tr[root].add=0;
    }
    void U_M(int root,int il,int ir,int l,int r,ll k){
        if(r<il||ir<l) return ;
        if(l<=il&&ir<=r) {
            tr[root].v=(tr[root].v*k)%p,
            tr[root].mul=(tr[root].mul*k)%p,
            tr[root].add=(tr[root].add*k)%p; 
            return ;
        }
        pushdown(root,il,ir) ;
        int mid=(il+ir)/2 ;
        U_M(root*2,il,mid,l,r,k) ;
        U_M(root*2+1,mid+1,ir,l,r,k) ;
        tr[root].v=(tr[root*2].v+tr[root*2+1].v)%p ;
        return ;
    }
    void U_A(int root,int il,int ir,int l,int r,ll k){
        if(r<il||ir<l) return ;
        if(l<=il&&ir<=r) {
            tr[root].add=(tr[root].add+k)%p,
            tr[root].v=(tr[root].v+k*(ir-il+1))%p; 
            return ;
        }
        pushdown(root,il,ir) ;
        int mid=(il+ir)/2 ;
        U_A(root*2,il,mid,l,r,k) ;
        U_A(root*2+1,mid+1,ir,l,r,k) ;
        tr[root].v=(tr[root*2].v+tr[root*2+1].v)%p ;
        return ;
    }
    ll query(int root,int il,int ir,int l,int r){
        if(r<il||ir<l) return 0;
        if(l<=il&&ir<=r) {return tr[root].v;}
        pushdown(root,il,ir) ;
        int mid=(il+ir)/2 ;
        return (query(root*2,il,mid,l,r)+query(root*2+1,mid+1,ir,l,r))%p;
    }
    int main(){
        int n,m ;
        scanf("%d%d%d",&n,&m,&p); 
        for(int i=1;i<=n;++i) scanf("%d",&arr[i]) ; build(1,1,n) ;
        //for(int i=1;i<=n;++i) cout<<tr[i].v<<" " ; cout<<endl ;
        while(m--){
            int x,y,z; ll k;
            scanf("%d",&x) ;
            if(x==1)scanf("%d%d%lld",&y,&z,&k) , U_M(1,1,n,y,z,k) ;
            else if(x==2)scanf("%d%d%lld",&y,&z,&k) , U_A(1,1,n,y,z,k) ;
            else scanf("%d%d",&y,&z),printf("%lld
    ",query(1,1,n,y,z)) ;
            //for(int i=1;i<=n;++i) cout<<tr[i].v<<" " ; cout<<endl ;
        }
        return 0 ;
    }
    

    可持久化

    可持久化数据结构

  • 相关阅读:
    【转载】关于nginx以及内核参数的配置
    【转载】Oracle 中count(1) 、count(*) 和count(列名) 函数的区别
    【转载】GET和POST两种基本请求方法的区别
    Eureka的使用
    【转载】Spring Cloud全家桶主要组件及简要介绍
    java阶段学习目标
    【转载】java对象和byte数组互转,直接拿去用
    【转载】非对称加密过程详解(基于RSA非对称加密算法实现)
    Python 四种数值类型(int,long,float,complex)区别及转换
    Python2和Python3中print的不同点
  • 原文地址:https://www.cnblogs.com/tyqtyq/p/9906386.html
Copyright © 2020-2023  润新知