P3373 【模板】线段树 2

10 篇文章 0 订阅
5 篇文章 0 订阅

题目描述
如题,已知一个数列,你需要进行下面三种操作:

1.将某区间每一个数乘上x

2.将某区间每一个数加上x

3.求出某区间每一个数的和

输入格式
第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k

操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k

操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果

输出格式
输出包含若干行整数,即为所有操作3的结果。

输入输出样例
输入 #1 复制

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

输出 #1 复制

17
2

说明/提示
时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强_

Ac_code:

#include <bits/stdc++.h>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
typedef long long LL;
using namespace std;
const LL MAXN = 1e5+5;
LL n,m,mod;
LL sum[MAXN<<2];
LL tag1[MAXN<<2],tag2[MAXN<<2];
LL a[MAXN];
/*
sum: 存区间和
a:存原始数据
tag1: 乘v lazy tag
tag2: 加v lazy tag
*/
inline bool read(LL &x)
{
    char ch;
    int sign;
    if(ch = getchar(),ch==EOF) return 0;
    while(ch!='-'&&(!isdigit(ch))) ch = getchar();
    sign = (ch=='-')?-1:1;
    x = (ch=='-')?0:(ch-'0');
    while((ch=getchar())&&isdigit(ch))
        x = x*10 + (ch-'0');
    x *= sign;
    return 1;
}
inline void write(LL x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

inline void push_up(LL rt)
{
    sum[rt] = (sum[rt<<1]+sum[rt<<1|1])%mod;
    return;
}
void build(LL rt,LL l,LL r)
{
    //lazy tag初始化!!
    tag1[rt] = 1;
    tag2[rt] = 0;
    if(l == r)
    {
        sum[rt] = a[l];
        return;
    }
    LL mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}
inline void push_down(LL rt,LL l,LL r)
{
    LL mid = (l + r) >> 1;
    //要考虑优先级:不清楚加先还是乘先,而不同顺序结果不同
    if(tag1[rt]!=1) //可能乘0!!!
    {
        tag1[rt<<1] = (tag1[rt<<1] * tag1[rt]) % mod;
        sum[rt<<1] = (sum[rt<<1] * tag1[rt]) % mod;
        tag1[rt<<1|1] = (tag1[rt<<1|1] * tag1[rt]) % mod;
        sum[rt<<1|1] = (sum[rt<<1|1] * tag1[rt]) % mod;
        //乘标记下传同时要更新加标记(把即将加的算上)
        tag2[rt<<1] = (tag2[rt<<1] * tag1[rt]) % mod;
        tag2[rt<<1|1] = (tag2[rt<<1|1] * tag1[rt]) % mod;
        tag1[rt] = 1;//mulit init: 1!!
    }
    if(tag2[rt]>0)
    {
        tag2[rt<<1] = (tag2[rt<<1] + tag2[rt]) % mod;
        sum[rt<<1] = (sum[rt<<1] + (mid-l+1)*tag2[rt]%mod) % mod;
        tag2[rt<<1|1] = (tag2[rt<<1|1] + tag2[rt]) % mod;
        sum[rt<<1|1] = (sum[rt<<1|1] + (r-mid)*tag2[rt]%mod) % mod;
        tag2[rt] = 0;
    }
}
//操作区间【L,R】
void update_range(LL L,LL R,LL v,LL op,LL rt,LL l,LL r)
{
    if(L <= l && r <= R) //是修改区间子集
    {
        if(op == 1)
        {
            /*
            现在要对乘lazy tag修改,如果之前有加lazy tag
            由于乘优先级高,现在修改乘lazy tag,
            会影响加 lazy tag,此时需要同时修改加lazy tag,
            把将要加那部分,现在算进来
            */
            sum[rt] = (sum[rt] * v) % mod;
            tag1[rt] = (tag1[rt] * v) % mod; //tag1累乘
            tag2[rt] = (tag2[rt] * v) % mod;//是乘,加也变
        }
        else
        {
            /*
            现在要对加lazy tag修改,如果之前有乘lazy tag
            由于乘优先级高,现在修改加lazy tag,
            对最终结果不会有影响
            */
            sum[rt] = (sum[rt] + (r-l+1)*v%mod) % mod;
            tag2[rt] = (tag2[rt] + v) % mod;//tag2累加
        }
        return;
    }
    push_down(rt,l,r); //标记下传
    LL mid = (l + r) >> 1;
    if(L <= mid)
        update_range(L,R,v,op,lson);
    if(R > mid)
        update_range(L,R,v,op,rson);
    push_up(rt); //向上更新信息
}
LL query(LL L,LL R,LL rt,LL l,LL r)
{
    if(L <= l && r <= R)//查询区间子集
    {
        return sum[rt]%mod;
    }
    push_down(rt,l,r); //标记下传更新可能要涉及到的区间
    LL mid = (l + r) >> 1;
    LL res = 0;
    if(L <= mid) //左孩子涉及
        res = (res+query(L,R,lson))%mod;
    if(R > mid) //右孩子涉及
        res = (res+query(L,R,rson))%mod;
    return res%mod;
}
/*
取模过程中,也不能一味的取模,
有些不必要的就可以不取模,否则TLE!!!
*/
int main()
{
    //scanf("%lld%lld%lld",&n,&m,&mod);
    read(n),read(m),read(mod);
    for(LL i = 1; i <= n; i++)
    {
        //scanf("%lld",&a[i]);
        read(a[i]);
    }
    build(1,1,n);
    while(m--)
    {
        LL op,x,y,k;
        //scanf("%lld%lld%lld",&op,&x,&y);
        read(op),read(x),read(y);
        if(op == 3)
        {
            //printf("%lld\n",query(x,y,1,1,n));
            write(query(x,y,1,1,n));
            puts("");
        }
        else
        {
            //scanf("%lld",&k);
            read(k);
            update_range(x,y,k,op,1,1,n);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo Bliss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值