zoukankan      html  css  js  c++  java
  • NOIp 数据结构专题总结 (2):分块、树状数组、线段树

    系列索引:

    分块

    阅:《「分块」数列分块入门 1-9 by hzwer》 link


    树状数组 Binary Indexed Tree

    时间复杂度 每次操作 (O(log n)),空间复杂度 (O(n))

    Pure

    下标从 1 开始。

    ll t[N];
    
    inline int lowbit(int x) {return x&-x; }
    
    inline void update(int x, ll val) {
    	for (; x<=n; x+=lowbit(x)) t[x]+=val;
    }
    inline ll sum(int x) {
    	ll res=0;
    	for (; x; x-=lowbit(x)) res+=t[x];
    	return res;
    }
    

    2 Dimensions

    void update(int x, int y, int z) {
        int i = x;
        while (i <= n) {
            int j = y;
            while (j <= m) {
                t[i][j] += z;
                j += lowbit(j);
            }
            i += lowbit(i);
        }
    }
    
    int sum(int x, int y) { // prefix
        int res = 0, i = x;
        while (i > 0) {
            int j = y;
            while (j > 0) {
                res += t[i][j];
                j -= lowbit(j);
            }
            i -= lowbit(i);
        }
        return res;
    }
    

    线段树 Segment Tree

    时间复杂度 每次操作 (O(log n)),空间复杂度 (O(4n))

    Standard

    单点修改,区间求和:

    #define lson k<<1, l, mid
    #define rson k<<1|1, mid+1, r
    
    int t[N<<2];
    
    void modify(int k, int l, int r, int x) {
    	if (l==r&&l==x) {++t[k]; return; }
    	register int mid=l+r>>1;
    	if (x<=mid) modify(lson, x);
    	if (mid<x) modify(rson, x);
    	++t[k];
    }
    int query(int k, int l, int r, int x, int y) {
    	if (x<=l&&r<=y) return t[k];
    	register int mid=l+r>>1, res=0;
    	if (x<=mid) res+=query(lson, x, y);
    	if (mid<y) res+=query(rson, x, y);
    	return res;
    }
    

    区间修改,区间求和:

    ll t[N<<2], laz[N<<2];
    
    inline void add(int k, int l, int r, ll val) {
    	laz[k]+=val;
    	t[k]+=(r-l+1)*val;
    }
    inline void pd(int k, int l, int r, int mid) {
    	if (!laz[k]) return;
    	add(lson, laz[k]), add(rson, laz[k]);
    	laz[k]=0;
    }
    void modify(int k, int l, int r, int x, int y, ll val) {
    	if (x<=l&&r<=y) {add(k, l, r, val); return; }
    	int mid=l+r>>1;
    	pd(k, l, r, mid);
    	if (x<=mid) modify(lson, x, y, val);
    	if (mid<y) modify(rson, x, y, val);
    	t[k]=t[k<<1]+t[k<<1|1];
    }
    ll query(int k, int l, int r, int x, int y) {
    	if (x<=l&&r<=y) return t[k];
    	int mid=l+r>>1; ll res=0;
    	pd(k, l, r, mid);
    	if (x<=mid) res+=query(lson, x, y);
    	if (mid<y) res+=query(rson, x, y);
    	return res;
    }
    

    有运算优先级:如同时支持区间乘和区间加,将标记设计为先乘 a 再加 b,那么区间加时直接加 b 即可,而区间乘时需要将 a 和 b 都乘上一个数。

    int lazy[N<<2], sum[N<<2], lazy2[N<<2];
    
    void build(int k, int l, int r) {
    	lazy2[k] = 1;
    	if (l==r) { sum[k] = data[l]; return; }
    	int mid = l+r >> 1;
    	build(k<<1, l, mid);
    	build((k<<1)+1, mid+1, r);
    	sum[k] = ((ll)sum[k<<1] + sum[(k<<1)+1]) % MOD;
    }
    void add(int k, int l, int r, int v) {
    	lazy[k] = ((ll)lazy[k] + v) % MOD;
    	sum[k] = (sum[k] + (ll)(r-l+1) * v) % MOD;
    }
    void mul(int k, int l, int r, int v) {
    	lazy[k] = ((ll)lazy[k] * v) % MOD;
    	lazy2[k] = ((ll)lazy2[k] * v) % MOD;
    	sum[k] = ((ll)sum[k] * v) % MOD;
    }
    void pushdown(int k, int l, int r, int mid) {
    	if (lazy2[k]!=1) {
    		mul(k<<1, l, mid, lazy2[k]);
    		mul((k<<1)+1, mid+1, r, lazy2[k]);
    		lazy2[k] = 1;
    	}
    	if (lazy[k]) {
    		add(k<<1, l, mid, lazy[k]);
    		add((k<<1)+1, mid+1, r, lazy[k]);
    		lazy[k] = 0;
    	}
    }
    void modify(int k, int l, int r, int x, int y, int v) {
    	if (l>=x && r<=y) {add(k, l, r, v); return;}
    	int mid = l+r >> 1;
    	pushdown(k, l, r, mid);
    	if (x<=mid) modify(k<<1, l, mid, x, y, v);
    	if (y>mid) modify((k<<1)+1, mid+1, r, x, y, v);
    	sum[k] = ((ll)sum[k<<1] + sum[(k<<1)+1]) % MOD;
    }
    void modify2(int k, int l, int r, int x, int y, int v) {
    	if (l>=x && r<=y) {mul(k, l, r, v); return;}
    	int mid = l+r >> 1;
    	pushdown(k, l, r, mid);
    	if (x<=mid) modify2(k<<1, l, mid, x, y, v);
    	if (y>mid) modify2((k<<1)+1, mid+1, r, x, y, v);
    	sum[k] = ((ll)sum[k<<1] + sum[(k<<1)+1]) % MOD;
    }
    int query(int k, int l, int r, int x, int y) {
    	if (l>=x && r<=y) return sum[k];
    	int mid = l+r >> 1, res = 0;
    	pushdown(k, l, r, mid);
    	if (x<=mid) res = ((ll)res + query(k<<1, l, mid, x, y)) % MOD;
    	if (y>mid) res = ((ll)res + query((k<<1)+1, mid+1, r, x, y)) % MOD;
    	return res;
    }
    

    标记永久化

    对于树套树,主席树等使用到线段树的比较复杂的数据结构,打标记后 pushdown、pushup 是很费劲的。

    思路:不下传标记;在路过该节点的时候把修改对答案的影响加上,来省去标记下放的过程。


    zkw Segment Tree

    int zkw; ll t[N<<2], laz[N<<2];
    
    inline void update(int x, int y, ll val) {
    	ll l=0, r=0, f=1;
    	for (x+=zkw-1, y+=zkw+1; x^y^1; x>>=1, y>>=1, f<<=1) {
    		t[x]+=val*l, t[y]+=val*r;
    		if (~x&1) laz[x^1]+=val, t[x^1]+=val*f, l+=f;
    		if (y&1) laz[y^1]+=val, t[y^1]+=val*f, r+=f;
    	}
    	for (; x; x>>=1, y>>=1) t[x]+=val*l, t[y]+=val*r;
    }
    
    inline ll query(int x, int y) {
    	ll res=0, l=0, r=0, f=1;
    	for (x+=zkw-1, y+=zkw+1; x^y^1; x>>=1, y>>=1, f<<=1) {
    		if (laz[x]) res+=laz[x]*l; 
    		if (laz[y]) res+=laz[y]*r;
    		if (~x&1) res+=t[x^1], l+=f;
    		if (y&1) res+=t[y^1], r+=f;
    	}
    	for (; x; x>>=1, y>>=1) res+=laz[x]*l, res+=laz[y]*r;
    	return res;
    }
    
    for (zkw=1; zkw<=n+1; zkw<<=1);
    for (int i=zkw+1; i<=zkw+n; i++) scanf("%lld", t+i);
    for (int i=zkw-1; i>0; --i) t[i]=t[i<<1]+t[i<<1|1];
    


    Post author 作者: Grey
    Copyright Notice 版权说明: Except where otherwise noted, all content of this blog is licensed under a CC BY-NC-SA 4.0 International license. 除非另有说明,本博客上的所有文章均受 知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议 保护。
  • 相关阅读:
    CodeIgniter 2.X 于 PHP5.6 兼容错误
    解决 TextMate 2 无法安装 Emmet 插件
    Windows 10 KMS 激活方法
    Sublime Text 3 如何修改默认快捷键
    Grunt快速使用笔记
    CSS3字体发光效果
    CSS3使用盒模型实现三栏布局
    CSS3Transition添加多个过渡效果
    Javascript 判断网页横竖屏
    【iOS知识汇】OC点语法的坑
  • 原文地址:https://www.cnblogs.com/greyqz/p/ds2.html
Copyright © 2011-2022 走看看