[bzoj5039][线段树][lazy]序列维护

本文介绍了一种使用线段树和Lazy Tag解决数列操作问题的方法,包括乘法、加法及区间求和操作,并通过具体示例详细展示了算法实现过程。

【题意】

JYY 有一个维护数列的任务。 他希望你能够来帮助他完成。 JYY 现在有一个长度为 N 的序列 a1,a2,…,aN,有如下三种操作:
1、 把数列中的一段数全部乘以一个值; 2、 把数列中的一段数全部加上一个值; 3、 询问序列中的一段数的和。
由于答案可能很大,对于每个询问,你只需要告诉 JYY 这个询问的答案对 P 取模的结果即可。

【输入】

第一行包含两个正整数, N 和 P; 第二行包含 N 个非负整数,从左到右依次为 a1,a2,…,aN。 第三行有一个整数
M,表示操作总数。 接下来 M 行,每行满足如下三种形式之一: 1、“ 1 t g c”(不含引号)。表示把所有满足 t ≤ i ≤ g 的
ai 全部乘以 c; 2、“ 2 t g c”(不含引号)。表示把所有满足 t ≤ i ≤ g 的 ai 全部加上 c; 3、“ 3 t
g”(不含引号)。表示询问满足 t ≤ i ≤ g 的 ai 的和对 P 取模的值。 1 ≤ N,M ≤ 10^5, 1 ≤ P, c,
ai ≤ 2*10^9, 1 ≤ t ≤ g ≤ N

【输出】

对于每个以 3 开头的操作,依次输出一行,包含对应的结果。

【样例输入】
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
【样例输出】
2
35
8
【样例说明】

初始时数列为(1,2,3,4,5,6,7)。 经过第 1 次操作后,数列为(1,10,15,20,25,6,7)。 对第 2 次操作,和为
10+15+20=45,模 43 的结果是 2。 经过第 3 次操作后,数列为(1,10,24,29,34,15,16} 对第 4
次操作,和为 1+10+24=35,模 43 的结果是 35。 对第 5 次操作,和为 29+34+15+16=94,模 43 的结果是
8。

【题解】

最近学了莫队的我表示很喜欢序列的题,结果一看这是个线段树lazy..
具体的坑点就一个吧。。就是运算优先级的问题。乘法一定要在加法之前进行处理呢 之前没发现这个的我一直lazy结果T的爆炸。。
还有模数的问题也一定要想好啊,哪里要模一下哪里不用要想好 懒了点的我就在建树的时候顺便插点了。。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL mod;
struct node
{
    int l,r,lc,rc;
    LL c;
    LL mul,add;
    bool lazy;
    node(){lazy=false;}
}tr[210000];int trlen;
int n,m;int x[110000];
void bt(int l,int r)
{
    int now=++trlen;
    tr[now].l=l;tr[now].r=r;
    tr[now].lc=tr[now].rc=-1;tr[now].c=0;
    tr[now].mul=1;tr[now].add=0;
    if(l==r){tr[now].c=x[l]%mod;return ;}
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=trlen+1; bt(l,mid);
        tr[now].rc=trlen+1; bt(mid+1,r);
    }
    tr[now].c=tr[tr[now].lc].c+tr[tr[now].rc].c;
}
void lazy(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    int l=tr[x].l,r=tr[x].r;int mid=(l+r)/2;
    if(lc!=-1)
    {
        tr[lc].c=(tr[lc].c*tr[x].mul)%mod;
        tr[lc].c=(tr[lc].c+tr[x].add*(mid-l+1))%mod;
        tr[lc].mul=(tr[lc].mul*tr[x].mul)%mod;
        tr[lc].add=(tr[lc].add*tr[x].mul)%mod;
        tr[lc].add=(tr[lc].add+tr[x].add)%mod;
    }
    if(rc!=-1)
    {
        tr[rc].c=(tr[rc].c*tr[x].mul)%mod;
        tr[rc].c=(tr[rc].c+tr[x].add*(r-mid))%mod;
        tr[rc].mul=(tr[rc].mul*tr[x].mul)%mod;
        tr[rc].add=(tr[rc].add*tr[x].mul)%mod;
        tr[rc].add=(tr[rc].add+tr[x].add)%mod;
    }
    tr[x].mul=1;tr[x].add=0;
}
void change_x(int now,int l,int r,LL c)//l~r +c
{
    if(tr[now].l==l && tr[now].r==r)
    {
        tr[now].c=(tr[now].c+(r-l+1)*c)%mod;
        tr[now].add=(tr[now].add+c)%mod;
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    lazy(now);
    if(r<=mid)change_x(lc,l,r,c);
    else if(mid+1<=l)change_x(rc,l,r,c);
    else 
    {
        change_x(lc,l,mid,c);
        change_x(rc,mid+1,r,c);
    }
    tr[now].c=(tr[lc].c+tr[rc].c)%mod;
}
void change_a(int now,int l,int r,LL c)//l~r *c
{
    if(tr[now].l==l && tr[now].r==r)
    {
        tr[now].c=(tr[now].c*c)%mod;
        tr[now].mul=(tr[now].mul*c)%mod;
        tr[now].add=(tr[now].add*c)%mod;
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    lazy(now);
    if(r<=mid)change_a(lc,l,r,c);
    else if(mid+1<=l)change_a(rc,l,r,c);
    else 
    {
        change_a(lc,l,mid,c);
        change_a(rc,mid+1,r,c);
    }
    tr[now].c=(tr[lc].c+tr[rc].c)%mod;
}
LL findsum(int now,int l,int r)
{
    if(tr[now].l==l && tr[now].r==r)
    {
        lazy(now);
        return tr[now].c%mod;
    }
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    lazy(now);
    if(r<=mid)return findsum(lc,l,r);
    else if(mid+1<=l)return findsum(rc,l,r);
    else return ((findsum(lc,l,mid)%mod+findsum(rc,mid+1,r)%mod)%mod);
}
int main()
{
    scanf("%d%lld",&n,&mod);
    for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
    trlen=0;bt(1,n);
    scanf("%d",&m);
    while(m--)
    {
        int q,t,g;LL c;
        scanf("%d%d%d",&q,&t,&g);
        if(q==3){printf("%lld\n",findsum(1,t,g)%mod);continue;}
        scanf("%lld",&c);
        if(q==1)change_a(1,t,g,c);
        else change_x(1,t,g,c);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值