zoukankan      html  css  js  c++  java
  • 关于线段树的那些奇技淫巧

    目录对你说:我在右边
    如果你不会线段树,戳这里

    维护区间max/min值:

    这就是push_up()浅显易懂.

    void push_up(int rt) {
    	tree[rt].max = max(tree[lson].max, tree[rson].max);
    	tree[rt].min = min(tree[lson].min, tree[rson].min);
    }
    

    建树的时候就那样建,push_down的时候看一下max和min都改成lazy就行了.
    有的时候用不到push_down();

    query

    也很好懂,和求区间和差不多.
    有修改操作的时候可以加上pushd_down();

    int query_max(int rt, int l, int r, int L, int R) {
    	if (L <= l && r <= R) return tree[rt].max;
    	int mid = (l + r) >> 1, maxn = -1;
    	if (L <= mid) maxn = max(maxn, query_max(lson, l, mid, L, R));
    	if (R > mid) maxn = max(maxn, query_max(rson, mid + 1, r, L, R));
    	return maxn;
    }
    

    维护区间和+区间开平方.

    这是个好题
    强烈推荐GSS毒瘤数据结构,在洛谷可以搜到,做完之后会感觉自己的思维得到了升华.
    A一题提神醒脑,A俩题永不疲劳,A仨题长生不老
    因为一个大整数(sqrt{sqrt{sqrt{sqrt{sqrt{sqrt{n}}}}}} = 1)我们可以用lazy判断一下这个区间开了几次,如果大于等于6的话就没必要搞了(然而我没写)
    而且开方的时候判断一下区间的最大值是不是1,如果区间的最大值都是1了,那么其他的必然也是1,就没有必要在开方了.

    关于更新:

    void update(int L, int R, int l, int r, int rt) {
        if (l == r) {
            t[rt].sum = sqrt(t[rt].sum);
            t[rt].mx = sqrt(t[rt].mx);
            return ;
        }
        int m = (l + r) >> 1;
        if (L <= m && t[lson].mx > 1) update(L, R, l, m, lson);
        if (R > m && t[rson].mx > 1) update(L, R, m + 1, r, rson);
        pushup(rt);
    }
    

    查询就不用说的吧,和区间求最大值一样啊。

    区间最大子段和.

    好题 可以说是裸题.
    难点在于push_up和query

    push_up

    void push_up(int rt) {
    	tree[rt].sum = tree[lson].sum + tree[rson].sum;//这就是普通的区间和
    	
    	tree[rt].qian = max(tree[lson].qian, tree[lson].sum + tree[rson].qian);
    	tree[rt].hou = max(tree[rson].hou, tree[rson].sum + tree[lson].hou);
    	//区间的前缀和的最大值就是左区间的前缀和和(右区间的全缀合加上左区间的和)取max
    	//区间后缀和类比可得
    	tree[rt].zi = max(tree[lson].zi, tree[rson].zi);
    	tree[rt].zi = max(tree[rt].zi, tree[lson].hou + tree[rson].qian);
    	//最大子段和就是左右区间最大子段和,还有左区间后缀和加上右区间前缀和取max
    }
    

    关于query:

    node unioc(node a, node b) {
    	node ans;//区间合并可类比push_up
    	ans.sum = a.sum + b.sum;
    	ans.zi = max(a.zi, b.zi);
    	ans.zi = max(a.hou + b.qian, ans.zi);
    	ans.qian = max(a.qian, a.sum + b.qian);
    	ans.hou = max(b.hou, b.sum + a.hou);
    	return ans;
    }
    
    node query(int rt, int l, int r, int L, int R) {
    	if (L <= l && r <= R) return tree[rt];
    	int mid = (l + r) >> 1;
    	if (L > mid) return query(rson, mid + 1, r, L, R);//如果这个区间都在右边,直接在右边算就行
    	if (R <= mid) return query(lson, l, mid, L, R);//争端区间都在左边
    	return unioc(query(lson, l, mid, L, R), query(rson, mid + 1, r, L, R));//如果两端都有那么还要合并区间.
    }
    
    

    加上区间修改就变成了这个题
    也挺简单的,只要你会了上边那个.
    区间修改也挺简单的.通俗易懂。

    void change(int rt, int c, int l, int r, int pos) {
    	if (l == r) {
    		tree[rt].sum = tree[rt].ls = tree[rt].rs = tree[rt].mis = c;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (pos <= mid) change(lson, c, l, mid, pos);
    	if (pos > mid) change(rson, c, mid + 1, r, pos);
    	push_up(rt);
    }
    

    维护区间gcd

    我们都知道(gcd(a, b) = gcd(b, a-b))(更相减损术)
    我们将上述式子扩展到3项我们就会发现
    (gcd(a,b,c)=gcd(b,b−a,c−b))
    很明显这就是一个差分数组
    我们只需要开一棵线段树来维护差分数组gcd
    但是我们发现最前边的这个b我们维护的是当前这个点的差分值
    所以这就说明我们还需要开一个东西来维护每一个点的值
    而且还要支持区间修改(树状数组是一个好东西)

    关于push_up

    其实和求区间最大值差不多

    void push_up(int rt) {
    	tree[rt].gcd = abs(gcd(tree[lson].gcd, tree[rson].gcd));
    }
    

    关于查询

    我们需要查询一个最前边的值的大小,还有剩下的值的gcd
    然后两者求一个gcd

    ll query(int rt, int l, int r, int L, int R) {
    	if (L <= l && r <= R) return tree[rt].gcd;
    	int mid = (l + r) >> 1;ll g = 0;
    	if (L <= mid) g = abs(gcd(g, query(lson, l, mid, L, R)));
    	if (R > mid) g = abs(gcd(g, query(rson, mid + 1, r, L, R)));
    	return g;
    }
    ll ask(int b) { 
          ll ans = 0; 
          while (b) 
                ans += t[b], b -= lowbit(b); 
          return ans; 
    }
    
    x = read(), y = read();
    ll ans = query(1, 1, n + 1, x + 1, y);
    printf("%lld
    ", abs(gcd(ans, ask(x))));
    

    总结(思考方向):

    以后做线段树的时候考虑怎么上传,怎么下放,区间查询的时候需不需要合并.
    合并的时候需要注意什么问题.

    【未完待续】

  • 相关阅读:
    linux反汇编
    Java中UML图
    Java设计模式_创建型模式_单例模式
    Javadoc注释的用法
    VIM使用技巧1
    手动破解的 Linux下的Maltab 2014b
    让vim的在输入模式下现实光标不同
    Vim 自动补全成对的括号和引号
    MAMP:在 OSX 中搭建 Apache, MySQL, PHP 环境并本地安装、调试 WordPress
    MAC+iTerm定制目录显示颜色和提示符
  • 原文地址:https://www.cnblogs.com/zzz-hhh/p/12214977.html
Copyright © 2011-2022 走看看