HDU - 4578 Transformation(线段树 - 区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578

题意:初始时给一个全为0的数列,有四种操作
(1)将x->y的数全部加上c;
(2)将x->y的数全部乘以c;
(3)将x->y的数全部赋值为c;
(4)查询x->y的每个数的p次方的和;

思路:这一题涉及到特别多的操作,但都是区间上的操作,所以可以想到线段树,但这一题我吗要打三个标记(lazy,lazyadd,lazymul,分别是区间替换,期间加和区间乘)再用三个数组(sum1,sum2,sum3分别表示1次方,2次方和3次方),还要用两个数组(L,R记录左右端点)之后,就是令人窒息的push_down函数的编写啦。
首先,我们有三个标记,所以我们要确定一个运算的优先级。毫无疑问,lazy标记一定是优先级最高的,其次,乘法的优先级高于加法。为什么呢?

假设我们现在有三个操作,
第一个是给x->y区间+3,
第二个是给区间x->y *4,
第三个是给区间x->y +5;
那总的式子应该是 ∑ \sum_{} (a+3)*4+5。
如果我们优先算加法的话,那我们算的应该是 ∑ \sum_{} (a+3+5/4)*4;
如果我们先算乘法的话,我们算的就是 ∑ \sum_{} a*4+3*4+5;
很明显,加法优先会涉及到分数运算,会丢失精度,
但乘法优先可以很完美的避开这一点。
这样的话,每次我们更新lazymul是也要把lazyadd更新一下。

明确了这个之后,还有一个就是2次方和三次方该怎么表示。
首先如果是赋值和乘法的话,那就是直接乘就好啦,主要是加法

∑ ( x + c ) ∗ ( x + c ) \sum_{}(x+c)*(x+c) (x+c)(x+c)= ∑ x ∗ x + 2 ∗ c ∗ x + c ∗ c \sum_{}x*x+2*c*x+c*c xx+2cx+cc
= ∑ x ∗ x \sum_{}x*x xx+ ∑ 2 ∗ c ∗ x \sum_{}2*c*x 2cx+ ∑ c ∗ c \sum_{}c*c cc
=sum2+2*c*sum1+c*c*(区间长度);

同理 ∑ ( x + c ) ∗ ( x + c ) ∗ ( x + c ) \sum_{}(x+c)*(x+c)*(x+c) (x+c)(x+c)(x+c)
= ∑ x ∗ x ∗ x \sum_{}x*x*x xxx+ ∑ 3 ∗ c ∗ x ∗ x \sum_{}3*c*x*x 3cxx+ ∑ 3 ∗ c ∗ c ∗ x \sum_{}3*c*c*x 3ccx+ ∑ c ∗ c ∗ c \sum_{}c*c*c ccc
=sum3+3*c*sum2+3*c*c*sum1+c*c*c*(区间长度)

想到这,这道题就差不多可以解决啦,不过这个代码真的不怎么好些们一定要细心,细心,再细心。

具体代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn=1e5+7;
const LL mod=10007;

LL sum1[maxn<<2],sum2[maxn<<2],sum3[maxn<<2];
LL lazy[maxn<<2],lazyadd[maxn<<2],lazymul[maxn<<2];
LL L[maxn<<2],R[maxn<<2];

void push_up(int rt)
{
    sum1[rt]=(sum1[rt<<1]+sum1[rt<<1|1])%mod;
    sum2[rt]=(sum2[rt<<1]+sum2[rt<<1|1])%mod;
    sum3[rt]=(sum3[rt<<1]+sum3[rt<<1|1])%mod;
}

void build(int l,int r,int rt)
{
    lazy[rt]=lazyadd[rt]=0;
    lazymul[rt]=1;
    L[rt]=l,R[rt]=r;
    if(l==r)
    {
        sum1[rt]=sum2[rt]=sum3[rt]=0;
        return ;
    }
    int m=(l+r)>>1;
    build(lson),build(rson);
    push_up(rt);
}

void push_down(int rt)
{
    ///change
    if(lazy[rt])
    {
        lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
        ///lazy不为0的话要把lazyadd和lazymul标记清空
        lazyadd[rt<<1]=lazyadd[rt<<1|1]=0;
        lazymul[rt<<1]=lazymul[rt<<1|1]=1;

        sum1[rt<<1]=(lazy[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;
        sum2[rt<<1]=(lazy[rt]*lazy[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;
        sum3[rt<<1]=(lazy[rt]*lazy[rt]*lazy[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;

        sum1[rt<<1|1]=(lazy[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        sum2[rt<<1|1]=(lazy[rt]*lazy[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        sum3[rt<<1|1]=(lazy[rt]*lazy[rt]*lazy[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        
        ///更新完之后记得初始化
        lazy[rt]=0;
    }

    ///mul
    if(lazymul[rt]!=1)
    {
        lazymul[rt<<1]=(lazymul[rt<<1]*lazymul[rt])%mod;
        lazymul[rt<<1|1]=(lazymul[rt<<1|1]*lazymul[rt])%mod;
        
        ///更新lazymul标记时切记要把lazyadd标记也更新
        lazyadd[rt<<1]=(lazyadd[rt<<1]*lazymul[rt])%mod;
        lazyadd[rt<<1|1]=(lazyadd[rt<<1|1]*lazymul[rt])%mod;

        sum1[rt<<1]=(sum1[rt<<1]*lazymul[rt])%mod;
        sum2[rt<<1]=(sum2[rt<<1]*lazymul[rt]*lazymul[rt])%mod;
        sum3[rt<<1]=(sum3[rt<<1]*lazymul[rt]*lazymul[rt]*lazymul[rt])%mod;

        sum1[rt<<1|1]=(sum1[rt<<1|1]*lazymul[rt])%mod;
        sum2[rt<<1|1]=(sum2[rt<<1|1]*lazymul[rt]*lazymul[rt])%mod;
        sum3[rt<<1|1]=(sum3[rt<<1|1]*lazymul[rt]*lazymul[rt]*lazymul[rt])%mod;
        
        ///更新完之后记得初始化
        lazymul[rt]=1;
    }

    ///add
    if(lazyadd[rt])
    {
        lazyadd[rt<<1]=(lazyadd[rt<<1]+lazyadd[rt])%mod;
        lazyadd[rt<<1|1]=(lazyadd[rt<<1|1]+lazyadd[rt])%mod;
        
        ///根据推导的公式更新sum3,sum2和sum1
        sum3[rt<<1]=(sum3[rt<<1]+3LL*lazyadd[rt]*sum2[rt<<1]+3LL*lazyadd[rt]*lazyadd[rt]*sum1[rt<<1]+lazyadd[rt]*lazyadd[rt]*lazyadd[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;
        sum2[rt<<1]=(sum2[rt<<1]+2LL*lazyadd[rt]*sum1[rt<<1]+lazyadd[rt]*lazyadd[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;
        sum1[rt<<1]=(sum1[rt<<1]+lazyadd[rt]*(R[rt<<1]-L[rt<<1]+1LL))%mod;

        sum3[rt<<1|1]=(sum3[rt<<1|1]+3LL*lazyadd[rt]*sum2[rt<<1|1]+3LL*lazyadd[rt]*lazyadd[rt]*sum1[rt<<1|1]+lazyadd[rt]*lazyadd[rt]*lazyadd[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        sum2[rt<<1|1]=(sum2[rt<<1|1]+2LL*lazyadd[rt]*sum1[rt<<1|1]+lazyadd[rt]*lazyadd[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        sum1[rt<<1|1]=(sum1[rt<<1|1]+lazyadd[rt]*(R[rt<<1|1]-L[rt<<1|1]+1LL))%mod;
        
        ///更新完之后记得初始化
        lazyadd[rt]=0;
    }

}

void update(int L1,int R1,LL val,int l,int r,int rt)
{
    if(L1<=l&&r<=R1)
    {
        lazy[rt]=val,lazymul[rt]=1,lazyadd[rt]=0;
        sum1[rt]=(val*(R[rt]-L[rt]+1LL))%mod;
        sum2[rt]=(val*val*(R[rt]-L[rt]+1LL))%mod;
        sum3[rt]=(val*val*val*(R[rt]-L[rt]+1LL))%mod;
        return ;
    }
    push_down(rt);
    int m=(l+r)>>1;
    if(L1<=m) update(L1,R1,val,lson);
    if(R1>m) update(L1,R1,val,rson);
    push_up(rt);
}

void updateadd(int L1,int R1,LL val,int l,int r,int rt)
{
    if(L1<=l&&r<=R1)
    {
        lazyadd[rt]=(lazyadd[rt]+val)%mod;
        sum3[rt]=(sum3[rt]+3LL*val*sum2[rt]+3LL*val*val*sum1[rt]+val*val*val*(R[rt]-L[rt]+1LL))%mod;
        sum2[rt]=(sum2[rt]+2LL*val*sum1[rt]+val*val*(R[rt]-L[rt]+1LL))%mod;
        sum1[rt]=(sum1[rt]+(R[rt]-L[rt]+1LL)*val)%mod;
        return ;
    }
    push_down(rt);
    int m=(l+r)>>1;
    if(L1<=m) updateadd(L1,R1,val,lson);
    if(R1>m) updateadd(L1,R1,val,rson);
    push_up(rt);
}

void updatemul(int L1,int R1,LL val,int l,int r,int rt)
{
    if(L1<=l&&r<=R1)
    {
        lazymul[rt]=(lazymul[rt]*val)%mod;
        lazyadd[rt]=(lazyadd[rt]*val)%mod;
        sum1[rt]=(sum1[rt]*val)%mod;
        sum2[rt]=(sum2[rt]*val*val)%mod;
        sum3[rt]=(sum3[rt]*val*val*val)%mod;
        return ;
    }
    push_down(rt);
    int m=(l+r)>>1;
    if(L1<=m) updatemul(L1,R1,val,lson);
    if(R1>m) updatemul(L1,R1,val,rson);
    push_up(rt);
}

LL query(int L1,int R1,int opt,int l,int r,int rt)
{
    if(L1<=l&&r<=R1)
    {
        if(opt==1) return sum1[rt];
        if(opt==2) return sum2[rt];
        if(opt==3) return sum3[rt];
    }
    push_down(rt);
    LL ans=0;
    int m=(l+r)>>1;
    if(L1<=m) ans=(ans+query(L1,R1,opt,lson))%mod;
    if(R1>m) ans=(ans+query(L1,R1,opt,rson))%mod;
    return ans%mod;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)&&n&&m)
    {
        build(1,n,1);
        while(m--)
        {
            int opt,x,y;
            LL c;
            scanf("%d%d%d%lld",&opt,&x,&y,&c);
            if(opt==1) updateadd(x,y,c,1,n,1);
            else if(opt==2) updatemul(x,y,c,1,n,1);
            else if(opt==3) update(x,y,c,1,n,1);
            else printf("%lld\n",query(x,y,c,1,n,1));
        }
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值