HDU 4578 Transformation 题解(线段树)

这篇博客详细介绍了如何利用线段树解决区间加、乘、赋值操作以及求解一次、二次、三次方和的问题。通过维护不同类型的标记和值,博主展示了如何处理区间操作和查询,特别强调了更新顺序的重要性,以确保计算的正确性。

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

原链接
点我QωQ

题意简述

给定一个数列,要支持区间加,乘,赋值,和区间求一次,二次,三次方和。

数据

输入

第一行两个正整数 n , m n,m n,m n n n表示序列长度, m m m表示操作数。一个操作包含四个整数, o , l , r , x o,l,r,x o,l,r,x o = 1 , 2 , 3 o=1,2,3 o=1,2,3分别对应在 [ l , r ] [l,r] [l,r]内加/乘/改为 x x x o = 4 o=4 o=4表示询问 [ l , r ] [l,r] [l,r]之间的 x x x次方和(如果 o = 4 o=4 o=4,保证 x &lt; = 3 x&lt;=3 x<=3)。多组数据,如果 n = m = 0 n=m=0 n=m=0表示结束。

输出

对于每个 o = 4 o=4 o=4的询问,输出答案。

样例

输入
5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0
输出
307
7489

思路

这个题思路很明显,就是用线段树维护一下。就是代码量比较大,思维量也比较大。

首先我们要维护三个 l a z y t a g lazytag lazytag,三个值,三个 l a z y t a g lazytag lazytag分别命名为 a ( 即 a d d i t i o n ) , m ( 即 m u l i p l i c a t i o n ) , c ( 即 c h a n g e ) a(即addition),m(即muliplication),c(即change) a(addition),m(muliplication),c(change),分别表示加标记,乘标记,赋值标记。
三个值分别为 s 1 , s 2 , s 3 s1,s2,s3 s1,s2,s3,表示区间的一次方和,二次方和,三次方和。

当我们对区间进行赋值操作的时候,显然这优先级非常高,会覆盖掉所有的其他标记。此时设我们赋的值是 x x x,长度为 n n n,那么 s 1 = x ∗ n s1=x*n s1=xn, s 2 = x 2 ∗ n s2=x^2*n s2=x2n, s 3 = x 3 ∗ n s3=x^3*n s3=x3n.

当我们对区间进行乘法操作的时候,会对加法标记产生影响。设我们乘了 x x x,原来加标记是 a a a,表示我们加过 a a a,那么现在就要加 a x ax ax。同时,我们的 s 1 = s 1 ∗ x s1=s1*x s1=s1x, s 2 = s 2 ∗ x 2 s2=s2*x^2 s2=s2x2, s 3 = s 3 ∗ x 3 s3=s3*x^3 s3=s3x3。这十分显然。

最难的就是加法了。请系好安全带,前方高能(道路千万条,安全第一条)。设我们的区间是 l l l r r r的,那么我们 + x +x +x的时候,显然 s 1 = s 1 + x ∗ n s1=s1+x*n s1=s1+xn。那么 s 2 s_2 s2呢?
设原数组是 s s s,那么
s 2 = ∑ i = l r ( s i + x ) 由 s 2 的 定 义 得 = ∑ i = l r ( s i 2 + 2 s i x + x 2 ) 初 中 学 的 平 方 和 公 式 ( 不 会 去 找 初 中 老 师 ) = ( ∑ i = l r s i 2 ) + ( 2 x ∑ i = l r s i ) + ( x 2 ∗ n ) 拆 开 了 加 号 , 并 提 了 点 东 西 出 来 = s 2 + 2 x ∗ s 1 + x 2 ∗ n 由 定 义 得 s_2=\sum\limits_{i=l}^{r}(s_i+x)\\ 由s2的定义得\\ =\sum\limits_{i=l}^{r}(s_i^2+2s_ix+x^2)\\ 初中学的平方和公式(不会去找初中老师)\\ =(\sum\limits_{i=l}^{r}s_i^2)+(2x\sum\limits_{i=l}^{r}s_i)+(x^2*n)\\ 拆开了加号,并提了点东西出来\\ =s_2+2x*s_1+x^2*n\\ 由定义得 s2=i=lr(si+x)s2=i=lr(si2+2six+x2)=(i=lrsi2)+(2xi=lrsi)+(x2n)西=s2+2xs1+x2n
接下来求 s 3 s_3 s3
s 3 = ∑ i = l r ( s i + x ) 3 由 定 义 = ∑ i = l r ( s i 3 + 3 s i 2 x + 3 s i x 2 + x 3 ) 初 中 学 的 立 方 和 公 式 = ( ∑ i = l r s i 3 ) + ( 3 x ∑ i = l r s i 2 ) + ( 3 x 2 ∑ i = l r s i ) + x 3 ∗ n 拆 开 了 加 号 , 并 提 了 点 东 西 出 来 = s 3 + 3 x s 2 + 3 x 2 s 1 + x 3 ∗ n s3=\sum\limits_{i=l}^{r}(s_i+x)^3 \\ 由定义 \\ =\sum\limits_{i=l}^{r}(s_i^3+3s_i^2x+3s_ix^2+x^3)\\ 初中学的立方和公式\\ =(\sum\limits_{i=l}^{r}s_i^3)+(3x\sum\limits_{i=l}^{r}s_i^2)+(3x^2\sum\limits_{i=l}^{r}s_i)+x^3*n 拆开了加号,并提了点东西出来\\ =s_3+3xs_2+3x^2s_1+x^3*n s3=i=lr(si+x)3=i=lr(si3+3si2x+3six2+x3)=(i=lrsi3)+(3xi=lrsi2)+(3x2i=lrsi)+x3n西=s3+3xs2+3x2s1+x3n
到此,我们已经推出公式了。那么,到底谁先更新呢?
我们发现, s 3 s_3 s3的式子中涉及到所有的 s 1 , 2 , 3 s_{1,2,3} s1,2,3,所以肯定要先更新,要不然等别的更好了,就会出现错误的值。同理,其次是 s 2 s_2 s2,最后是 s 1 s_1 s1

然后求和的时候,判一下即可。代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
//Flandle_Scarlet is my wife
{
    #define N 100100
    #define mod 10007
    #define int long long
    class SegmentTree
    {
        private:
            struct node
            {
                int l,r;
                //区间左,右
                int s1,s2,s3;
                //一次,二次,三次的和
                int a,m,c;
                //加(add),乘(multiply),改(change)的标记
                //c=-1表示未进行过修改
            }tree[N<<2];
        public:
            #define ls index<<1
            #define rs index<<1|1

            #define L tree[index].l
            #define R tree[index].r
            #define S1 tree[index].s1
            #define S2 tree[index].s2
            #define S3 tree[index].s3
            #define A tree[index].a
            #define M tree[index].m
            #define C tree[index].c

            #define lL tree[ls].l
            #define lR tree[ls].r
            #define lS1 tree[ls].s1
            #define lS2 tree[ls].s2
            #define lS3 tree[ls].s3
            #define lA tree[ls].a
            #define lM tree[ls].m
            #define lC tree[ls].c

            #define rL tree[rs].l
            #define rR tree[rs].r
            #define rS1 tree[rs].s1
            #define rS2 tree[rs].s2
            #define rS3 tree[rs].s3
            #define rA tree[rs].a
            #define rM tree[rs].m
            #define rC tree[rs].c
            //奇怪的define(还是能看懂的吧。。。)

            void Update(int index)
            {
                S1=(lS1+rS1)%mod;
                S2=(lS2+rS2)%mod;
                S3=(lS3+rS3)%mod;
            }//和推上来
            void Build(int l,int r,int index)//建树
            {
                L=l,R=r;
                A=S1=S2=S3=0;
                M=1,C=-1;//设置初始值
                //注意M的初始值是1!!!
                if (l==r) return;
                int mid=(l+r)>>1;
                Build(l,mid,ls);
                Build(mid+1,r,rs);
                Update(index);
            }
            void Add1(int x,int index)
            {
                //上文中说的n在这里写成R-L+1的形式,表示区间长度
                A=(A+x)%mod;
                //A+=x
                S3=(S3+3*x*S2%mod+3*x*x%mod*S1%mod+(R-L+1)*x%mod*x%mod*x%mod)%mod;
                //S3=S3+3*x*S2+3*x*x*S1+n*x*x*x
                S2=(S2+2*x*S1%mod+(R-L+1)*x%mod*x%mod)%mod;
                //S2=S2+2*x*S1+n*x*x
                S1=(S1+x*(R-L+1))%mod;
                //S1=S1+x*n
            }
            void Mul1(int x,int index)
            {
                M=M*x%mod;
                A=A*x%mod;
                S3=S3*x%mod*x%mod*x%mod;
                //S3=S3*x^3
                S2=S2*x%mod*x%mod;
                //S2=S2*x^2
                S1=S1*x%mod;
                //S1=S1*x
            }
            void Change1(int x,int index)
            {
                C=x%mod;
                M=1,A=0;
                //有了赋值标记就珂以忽略剩下的所有标记了
                S3=(x*x%mod*x%mod)%mod*(R-L+1)%mod;
                S2=(x*x%mod)%mod*(R-L+1)%mod;
                S1=x*(R-L+1)%mod;
            }
            void PushDown(int index)
            {
                if (~C)
                {
                    Change1(C,ls);
                    Change1(C,rs);
                    C=-1;
                }
                if (M!=1)
                {
                    Mul1(M,ls);
                    Mul1(M,rs);
                    M=1;
                }
                if (A)
                {
                    Add1(A,ls);
                    Add1(A,rs);
                    A=0;
                }
            }//注意优先级
            void Add(int l,int r,int x,int index)
            {
                if (l>R or L>r) return;
                if (l<=L and R<=r)
                {
                    Add1(x,index);return;
                }
                PushDown(index);//千万不要忘了PushDown!
                Add(l,r,x,ls);
                Add(l,r,x,rs);
                Update(index);//非常明显的写法。。。
            }
            void Mul(int l,int r,int x,int index)
            {
                if (l>R or L>r) return;
                if (l<=L and R<=r)
                {
                    Mul1(x,index);return;
                }
                PushDown(index);//千万不要忘了PushDown!
                Mul(l,r,x,ls);
                Mul(l,r,x,rs);
                Update(index);//结构很清晰吧。。。
            }
            void Change(int l,int r,int x,int index)
            {
                if (l>R or L>r) return;
                if (l<=L and R<=r)
                {
                    Change1(x,index);return;
                }
                PushDown(index);//千万不要忘了PushDown!
                Change(l,r,x,ls);
                Change(l,r,x,rs);
                Update(index);//不想解释。。。
            }
            int Query(int l,int r,int p,int index)
            {
                if (l>R or L>r) return 0;
                if (l<=L and R<=r)
                {
                    if (p==1) return S1%mod;
                    else if (p==2) return S2%mod;
                    else return S3%mod;
                }
                PushDown(index);//尤其是这里不要忘了PushDown!(我这里忘了WA了三次)
                return (Query(l,r,p,ls)+Query(l,r,p,rs))%mod;
                //基本的左右递归。。。
            }
    }T;

    void Query()
    {
        int n,m;
        while(1)
        {
            scanf("%lld%lld",&n,&m);
            if (n==0 and m==0) break;
            T.Build(1,n,1);
            for(int i=1;i<=m;++i)
            {
                int o,l,r,x;
                scanf("%lld%lld%lld%lld",&o,&l,&r,&x);
                if (o==1)
                {
                    T.Add(l,r,x,1);
                }
                if (o==2)
                {
                    T.Mul(l,r,x,1);
                }
                if (o==3)
                {
                    T.Change(l,r,x,1);
                }
                if (o==4)
                {
                    printf("%lld\n",T.Query(l,r,x,1)%mod);
                }
            }
        }
    }
    void Main()
    {
        if (0)
        {
            freopen("in.txt","r",stdin);
            freopen("ans.txt","w",stdout);
        }
        Query();
    }
    #undef int//long long
    #undef N//100100
    #undef mod//10007
};
int main()
{
    Flandle_Scarlet::Main();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值