zoukankan      html  css  js  c++  java
  • 数据结构小结+模板

    $$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 ;
    }
    

    可持久化

    可持久化数据结构

  • 相关阅读:
    Python 类中方法的内部变量,命名加'self.'变成 self.xxx 和不加直接 xxx 的区别
    用foreach遍历 datagridView 指定列所有的内容
    treeView1.SelectedNode.Level
    YES NO 上一个 下一个
    正则 单词全字匹配查找 reg 边界查找 精确匹配 只匹配字符 不含连续的字符
    抓取2个字符串中间的字符串
    sqlite 60000行 插入到数据库只用不到2秒
    将多行文本以单行的格式保存起来 读和写 ini
    将秒转换成时间格式
    richtextbox Ctrl+V只粘贴纯文本格式
  • 原文地址:https://www.cnblogs.com/tyqtyq/p/9906386.html
Copyright © 2011-2022 走看看