zoukankan      html  css  js  c++  java
  • 线段树维护区间加、乘、赋值、平方和、立方和

    原理

    对于x点,有加法懒标记add和乘法懒标记mul,那么可以以以下两种方式维护元素的值:

    (x'=(x+add)*mul)或者(x'=x*mul+add)

    可以证明先乘后加要比先加后乘容易维护

    原来的乘、加法标记为:mul1、add1,后来要加上的乘、加法标记为:mul2、add2

    x的值变为: x.dat => (x.dat * mul2) + (x.r - x.l + 1) * add2;

    x的乘法标记变为: x.mul1 => x.mul1 * mul2

    x的加法标记变为: x.add1 => x.add1 * mul2 + add2

    在区间加乘的板子基础上,若(mul=0,add=c),则代表区间赋值为c

    至于维护立方和的操作,因为:

    ((a+b)^3=a^3+3a^2b+3ab^2+b^3)

    所以对于利用lazy_tag维护的x值(a*x+b),其立方和为:

    (a^3x^3+3a^2x^2b+3axb^2+b^3)

    所以可以先更新乘法,使得x变为ax,同时更新ax下的sum1和sum2以及sum3(即(ax,a^2x^2,a^3x^3)),最后再利用公式进行加法的更新

    例题

    HDU 4758 Transformation

    4个操作:

    1.将a~b都加上c

    2.将a~b都乘上c

    3.将a~b都变成c

    4.查询a~b的每个数的p次方的和。(p=1,2,3)

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5+5;
    typedef long long LL;
    const int p = 10007;
    int tot, num;
    int n, m, r,t,cases=0;
    
    int w[N], a[N], sum1[N * 4], sum2[N * 4], sum3[N * 4], lazy_mul[N * 4], lazy_add[N * 4]; // w[i]=j表示时间戳为i的点的值为j,a[]输入每个节点的值,dat线段树每个点权值,lazy线段树每个点的懒标记
    vector<int> mp[N];
    
    
    void solve(int rt,int len,int a,int b){   //a为add b为mul
        lazy_mul[rt] = 1ll*lazy_mul[rt] * b % p;
        lazy_add[rt] = 1ll*lazy_add[rt] * b % p;
        lazy_add[rt] = ((lazy_add[rt] + a) % p + p) % p;
        if(b!=1){   //先乘后加
            sum1[rt] = 1ll*sum1[rt] * b % p;
            sum2[rt] = (1ll*sum2[rt] * b % p) * b % p;
            sum3[rt] = ((1ll*sum3[rt] * b % p) * b % p) * b % p;
        }
        if(a!=0){
            int a2 = 1ll*a * a % p, a3 = 1ll*a2 * a % p;
            sum3[rt] = ((sum3[rt] + (LL)len * a3 % p) + p) % p;
            sum3[rt] = ((sum3[rt] + 3ll * (LL)sum2[rt] % p * a % p) + p) % p;
            sum3[rt] = ((sum3[rt] + 3ll * (LL)sum1[rt] % p * a2 % p) + p) % p;
            sum2[rt] = ((sum2[rt] + 2ll * (LL)sum1[rt] % p * a % p) + p) % p;
            sum2[rt] = ((sum2[rt] + (LL)len * a2 % p) + p) % p;
            sum1[rt] = ((sum1[rt] + (LL)len * a % p) + p) % p;
        }
    }
    
    void pushup(int rt) {
        sum1[rt] = (sum1[rt << 1] + sum1[rt << 1 | 1]) % p;
        sum2[rt] = (sum2[rt << 1] + sum2[rt << 1 | 1]) % p;
        sum3[rt] = (sum3[rt << 1] + sum3[rt << 1 | 1]) % p;
    }   
    
    // 建线段树,rt为根,l为rt点管辖的左边界, r为rt点管辖的有边界
    void build(int rt, int l, int r)
    {
        lazy_add[rt] = 0;
        lazy_mul[rt] = 1;
        if(l==r)
        {
            int temp = a[l];
            sum1[rt] = temp;
            sum2[rt] = (1ll*sum1[rt] * sum1[rt]) % p;
            sum3[rt] = (1ll*sum1[rt] * sum2[rt]) % p;
            return ; 
        }
        int mid=(l + r)>>1; 
        build(rt << 1, l, mid); 
        build(rt << 1 | 1, mid+1, r); 
        pushup(rt);
    }
    
    // 下传
    void pushdown(int rt, int l, int r)
    {
        int mid = (l + r) >> 1;
        solve(rt << 1, mid - l + 1, lazy_add[rt], lazy_mul[rt]);
        solve(rt << 1 | 1, r - mid, lazy_add[rt], lazy_mul[rt]);
        lazy_add[rt] = 0;
        lazy_mul[rt] = 1;
    }
    
    // rt为根,l为rt点管辖的左边界, r为rt点管辖的有边界, L为需要修改的左区间,R为需要修改的右区间
    void modify(int rt, int l, int r, int L, int R, int a,int b)
    {
        if(L <= l && r <= R)
        {
            solve(rt, r - l + 1, a, b);
            return ; 
        } 
        pushdown(rt, l, r); 
        int mid = (l + r)>>1; 
        if(L <= mid) modify(rt << 1, l, mid, L, R, a,b); 
        if(mid < R) modify(rt << 1 | 1, mid + 1, r, L, R, a,b); 
        pushup(rt);
    }
    
    // rt为根,l为rt点管辖的左边界, r为rt点管辖的有边界, L为需要查询的左区间,R为查询的右区间,k代表查询的是k次方和
    int query(int rt, int l, int r, int L, int R,int k)
    {
        if(L <= l && r <= R)
        {
            if(k==1)
                return sum1[rt];
            if(k==2)
                return sum2[rt];
            if(k==3)
                return sum3[rt];
        }
        pushdown(rt, l, r); 
        int mid = (l + r)>>1; 
        int ans = 0; 
        if(L <= mid) ans += query(rt << 1, l, mid, L, R,k), ans %= p; 
        if(mid < R) ans += query(rt << 1 | 1, mid + 1, r, L, R,k), ans %= p;
        pushup(rt);
        return ans; 
    }
    
    
    int main()
    {
        while(scanf("%d%d", &n,&m)&&(n+m!=0)){
    
            for(int i=1; i<=n; i++) a[i]=0; // 读入每个点的权值
            
            build(1, 1, n);  
            // m次询问
            for(int i=1, op, x, y, z; i<=m; i++)
            {
                scanf("%d", &op); 
                if(op == 1)
                {
                    scanf("%d%d%d", &x, &y, &z); 
                    modify(1,1,n,x, y, z,1); // 区间[x,y]加上z
                }
                else if(op == 2)
                {
                    scanf("%d%d%d", &x, &y, &z); 
                    modify(1,1,n,x, y, 0,z); // 区间[x,y]乘上z
                }
                else if(op == 3)
                {
                    scanf("%d%d%d", &x, &y, &z); 
                    modify(1,1,n,x, y, z,0); // 区间[x,y]赋值为z
                }
                else 
                {
                    scanf("%d%d%d", &x,&y,&z); 
                    printf("%d
    ", query(1,1,n,x,y,z)); 
                }
            }
        } 
        
    
        return 0;
    }
    
  • 相关阅读:
    c#修改config中的AppSettings属性
    C​#​小​实​例​之​-​-​-​C​#​判​断​网​络
    走过2013,走进2014
    前端工程师常去的网站
    正则表达式中文匹配
    浅谈 -webkit-tap-highlight-color 属性
    JS前端开发判断是否是手机端并跳转操作(小结)(转)
    swf文件加密基础(转)
    Object与Dictionary的区别
    关于事件机制的理解
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14046275.html
Copyright © 2011-2022 走看看