Loading

线段树维护区间加、乘、赋值、平方和、立方和

原理

对于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\n", query(1,1,n,x,y,z)); 
            }
        }
    } 
    

    return 0;
}
posted @ 2020-11-27 09:36  dyhaohaoxuexi  阅读(684)  评论(0编辑  收藏  举报