【线段树多符号区间更新】HDU - 4578 Transformation

本文介绍了一种基于线段树的数据结构实现,用于处理数组区间上的加法、乘法、赋值及幂次和查询操作。通过维护不同级别的区间操作状态,并在遍历时应用这些状态,实现了高效的区间更新和查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description

输入n,m。给你长度为n的数组,初始化为0。接下里有m行操作:
(1)”1 x y c”,代表 把区间 [x,y] 上的值全部加c

(2)”2 x y c”,代表 把区间 [x,y] 上的值全部乘以c

(3)”3 x y c” 代表 把区间 [x,y]上的值全部赋值为c

(4)”4 x y p” 代表 求区间 [x,y] 上值的p次方和1<=p<=3

思路:

参考了一个我觉得很好的博客,但是有点不足的是,它的立方和那里错了
链接:http://www.cnblogs.com/GBRgbr/archive/2013/08/13/3254442.html。建议在敲的时候先别取mod,这样代码会清晰特别特别多。等敲好了,样例过了。在取模,这样就不会乱了,思路清晰还是很好敲的。

#include<bits/stdc++.h>
using namespace std;
#define lson root<<1
#define rson root<<1|1
#define MID int mid = (l + r) / 2
#define N 100000
#define mod 10007
int sum[3][N<<2], flag[2][N<<2];//sum[0]-[2]分别代表一次,二次,三次方后的和。
//flag[0]标记+了多少,flag[1]标记乘了多少
void build(int root, int l, int r)//初始化
{
    flag[0][root] = 0;
    flag[1][root] = 1;//乘1还是本身
    sum[0][root] = sum[1][root] = sum[2][root] = 0;
    if(l == r)
        return;
    MID;
    build(lson, l, mid);
    build(rson, mid + 1, r);
}
void Merge(int root, int l, int r)//归并和
{
    for(int i = 0; i < 3; i++)
    {
        sum[i][root] = (sum[i][lson] + sum[i][rson]) % mod;
    }
}
void add(int root, int l, int r, int c)//区间l-r都加c
{
    if(c){
    int s0 = sum[0][root], s1 = sum[1][root];
    //这些公式 纸上推一推就出来了
    sum[0][root] = (s0 + ((r-l+1)%mod * c) % mod) % mod;
    //取掉mod后就特别清晰 s0+(r-l+1)*c
    sum[1][root] = (s1 + ((2*c)%mod * s0)%mod + ((((r-l+1)%mod*c)%mod)*c%mod)%mod) % mod;
    //s1+(2*c)*s0+(r-l+1)*c*c
    sum[2][root] = (sum[2][root] + (3*c%mod * s1)%mod + ((3*c%mod * c)%mod * s0)%mod + ((((r-l+1)%mod * c)%mod * c)%mod * c)%mod)%mod;
    //sum[2][root]+3*c*s1+3*c*c*s0+(r-l+1)*c*c*c
    }
    return;
}
void multi(int root, int l, int r, int v)//区间l-r都乘v
{
    if(v != 1){//不是1才乘,乘法就特别简单了。
        int x = v;
        sum[0][root] *= x;// 乘了x一次方
        sum[0][root] %= mod;
        x *= v;
        x %= mod;
        sum[1][root] *= x;// 乘了x二次方
        sum[1][root] %= mod;
        x *= v;
        x %= mod;
        sum[2][root] *= x;// 乘了x立方
        sum[2][root] %= mod;
    }
    return;
}
void pushdown(int root, int l, int r)//先乘法后加法。
{
    MID;
    if(flag[1][root] != 1)
    {
        flag[1][lson] *= flag[1][root];
        flag[1][lson] %= mod;
        flag[1][rson] *= flag[1][root];
        flag[1][rson] %= mod;
        flag[0][lson] *= flag[1][root];
        flag[0][lson] %= mod;
        flag[0][rson] *= flag[1][root];
        flag[0][rson] %= mod;
        multi(lson, l, mid, flag[1][root]);
        multi(rson, mid + 1, r, flag[1][root]);
        flag[1][root] = 1;
    }
    if(flag[0][root] != 0)
    {
        flag[0][lson] += flag[0][root];
        flag[0][lson] %= mod;
        flag[0][rson] += flag[0][root];
        flag[0][rson] %= mod;
        add(lson, l, mid, flag[0][root]);
        add(rson, mid + 1, r, flag[0][root]);
        flag[0][root] = 0;
    }
    return;
}
void updata(int root, int l, int r, int ul, int ur, int v, int ok)
{
    if(ul <= l && r <= ur)
    {
        if(ok == 1)//区间加
        {
            flag[0][root] += v;
            flag[0][root] %= mod;
            add(root, l, r, v);//更新sum
        }
        else if(ok == 2)//区间乘
        {
            flag[0][root] *= v;//将区间加更新,这样pushdown的时候就先乘后加
            flag[0][root] %= mod;
            flag[1][root] *= v;
            flag[1][root] %= mod;
            multi(root, l, r, v);//更新sum
        }
        else//区间全部赋值为v
        {
            flag[0][root] = v;
            flag[1][root] = 0;//为0,因为下面的都没用了。pushdown的时候可以更新掉。
            multi(root, l, r, 0);//先乘
            add(root, l, r, v);//再加
        }
        return;
    }
    pushdown(root, l, r);
    MID;
    if(ul <= mid) updata(lson, l, mid, ul, ur, v, ok);
    if(ur > mid) updata(rson, mid + 1, r, ul, ur, v, ok);
    Merge(root, l, r);
}
int query(int root, int l, int r, int ul, int ur, int p)
{
    if(ul <= l && r <= ur)
    {
        return sum[p-1][root];//看p就几次方的和
    }
    pushdown(root, l, r);
    MID;
    int red = 0;
    if(ul <= mid) red += query(lson, l, mid, ul, ur, p) % mod;
    if(ur > mid) red += query(rson, mid + 1, r, ul, ur, p) % mod;
    return red;
}
int main()
{
    int n, m, ok, ul, ur, v;
    while(~scanf("%d %d", &n, &m))
    {
        if(!n && !m) break;
        build(1, 1, n);
        while(m--)
        {
            scanf("%d %d %d %d", &ok, &ul, &ur, &v);
            if(ok == 4)
            {
                printf("%d\n", query(1, 1, n, ul, ur, v) % mod);
            }
            else
            {
                updata(1, 1, n, ul, ur, v, ok);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值