线段树(标记下传乘法和加法)

传送门:https://www.luogu.org/problem/P3373

标记下传,这种东西就是在求和和更改的时候进行pushdown把标记(各种标记,类似于寒冰标记、痛苦标记、穹妹标记……)下传,来节省时间。

还是挺简单的,主要问题处在pushdown上但多看看就会了

#include<bits/stdc++.h>
using namespace std;
long long n,m,p;
long long multag[1000009];
long long addtag[1000009];
long long seg[4000009];
long long num[1000009];
inline long long kd()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}
inline long long ls(long long x){return x<<1;}
inline long long rs(long long x){return x<<1|1;}
void build(long long now,long long l,long long r)
{
    addtag[now]=0;
    multag[now]=1;
    if(l==r)
    {
        seg[now]=num[l]%p;
        return;
    }
    long long mid=(l+r)>>1;
    build(ls(now),l,mid);
    build(rs(now),mid+1,r);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
void pushdown(long long now,long long l,long long r)
{
    long long mid=(l+r)>>1;
    seg[ls(now)]=(seg[ls(now)]*multag[now]+addtag[now]*(mid-l+1))%p;
    seg[rs(now)]=(seg[rs(now)]*multag[now]+addtag[now]*(r-mid))%p;
    multag[ls(now)]=(multag[ls(now)]*multag[now])%p;
    multag[rs(now)]=(multag[rs(now)]*multag[now])%p;
    addtag[ls(now)]=(addtag[ls(now)]*multag[now]+addtag[now])%p;
    addtag[rs(now)]=(addtag[rs(now)]*multag[now]+addtag[now])%p;
    multag[now]=1;
    addtag[now]=0;
}
void mulchange(long long now,long long l,long long r,long long a,long long b,long long c)
{
    if(r<a||l>b)return;

    if(a<=l&&r<=b)
    {
        seg[now]=(seg[now]*c)%p;
        multag[now]=(multag[now]*c)%p;
        addtag[now]=(addtag[now]*c)%p;
        return;
    }
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    mulchange(ls(now),l,mid,a,b,c);
    mulchange(rs(now),mid+1,r,a,b,c);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
void addchange(long long now,long long l,long long r,long long a,long long b,long long c)
{
    if(r<a||l>b)return;
    if(l>=a&&r<=b)
    {
        addtag[now]=(addtag[now]+c)%p;
        seg[now]=(seg[now]+c*(r-l+1))%p;
        return;
    }
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    addchange(ls(now),l,mid,a,b,c);
    addchange(rs(now),mid+1,r,a,b,c);
    seg[now]=(seg[ls(now)]+seg[rs(now)])%p;
    return;
}
long long query(long long now,long long l,long long r,long long a,long long b)
{
    if(r<a||l>b)return 0;
    if(l>=a&&r<=b)return seg[now];
    pushdown(now,l,r);
    long long mid=(l+r)>>1;
    return (query(ls(now),l,mid,a,b)+query(rs(now),mid+1,r,a,b))%p;
}
int main()
{
    n=kd(),m=kd(),p=kd();
    for(register int i=1;i<=n;i++)
    {
        num[i]=kd();
    }
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        long long pan=kd();
        long long a=kd(),b=kd();
        if(pan==1)
        {
            long long c=kd();
            mulchange(1,1,n,a,b,c);
        }
        if(pan==2)
        {
            long long c=kd();
            addchange(1,1,n,a,b,c);
        }
        if(pan==3)
        {
            cout<<query(1,1,n,a,b)<<endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/1129-tangqiyuan/p/11754203.html

时间: 2024-10-19 20:40:33

线段树(标记下传乘法和加法)的相关文章

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树多重标记下传)

www.cnblogs.com/shaokele/ bzoj1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值

【BZOJ1513】【POI2006】Tet-Tetris 3D 二维线段树+标记永久化

题解:题意很裸啊~~~ 培训的时候说要写标记永久化,反正永久化很水,就直接写了. 但是我并不知道为什么要永久化,或者说理解不深刻,但是再遇上肯定能分析出来233. 大概应该可能或许就是: 直接原因:下传标记传不下去. 根本原因: 线段树有两层,这样它的传递可能就有点像拓扑了 就是外层线段树需要往内层线段树传,然后内层线段树还要下传 这样扫到某处时发现,****,还需要顺着两边的标记路径回溯到根, 然后各种压栈啊什么的,才能传下去,而且大概率出错(代码错或思路错). 所以简简单单写个标记永久化好了

[P2894][USACO08FEB] 酒店Hotel (线段树+懒标记下传)

题意:有 n个房间,题目给出两个操作,若 op==1,就输出最靠左的连续空房数量为 x的房间序列的最左边的序号,然后将这些房间改为入住:若 op==2,就将从 x~y的的序列全部改为空房: 解法:线段树+懒标记下传: 1.线段树:题目让在一个很长的序列操作很多次,暴力显然过不了,要用线段树优化: 2.懒标记下传:这是本题的精髓.   此题要维护的不是序列上的数值总和,而是该序列的最长连续空房的长度 sum,从该节点从左向右数最长的连续空房长度 lm,以及从该节点从右向左数最长的连续空房长度 rm

POJ 2155 Matrix 二维线段树+标记永久化?

题意:链接 方法:二维线段树+标记永久化 解析:题意是比较明朗的,算一下内存发现4000^2也是可以接受的,于是就开始yy二维线段树? 对于第一层x的线段树,每个节点开一整棵y线段树. 用个二维数组就实现了? 不过发现个问题啊,这题怎么pushdown啊,标记传不下去啊.如果给x打个标记那么怎么知道y传那段啊? 于是就学了新的东西?标记永久化. 本题是单点查询嘛,标记永久化就应该是把相应的区间直接异或,不用往下传?那查询的时候怎么办呢?只需要在查询的时候把所有路过该点的区间都异或起来就OK了.貌

线段树标记永久化

前言 对于树套树,主席树等使用到线段树的比较复杂的数据结构,如果区间修改的话,打标记后pushdown或者pushup是很难做到的完全不行吧 所以这个时候,一个神奇的东西诞生了... 正题 线段树标记永久化,维护一个标记,假设为cov,再维护一个sum 假设修改区间[ql, qr]全部加上v: 和平常一样,到这个区间后cov[x] += v 但是我们又不想pushup,怎么办? 很好做,更新的时候每次sum[x] += v * (qr - ql + 1) (注意这里的qr,ql是完全被包含于线段

[知识点]线段树标记永久化

前言: 本文由Hallmeow原创,转载请注明出处! 由于打丧心病狂的 [BZOJ 4826]影魔  导致需要学习标记永久化,于是入坑OvO 知识点:线段树标记永久化 对于树套树,主席树等使用到线段树的比较复杂的数据结构,如果我们区间修改的话,打标记后pushdown或者pushup是很费劲的 那么我们能不能不用pushdown和pushup呢?当然可以啦!这样就用到标记永久化了! 原理就是: 在路过该节点的时候把修改对答案的影响加上,来省去标记下放的过程 实现起来: 线段树的每个节点维护 su

清华集训 2014--奇数国(线段树&amp;欧拉函数&amp;乘法逆元&amp;状态压缩)

昨天想了一晚...早上AC了... 这么长时间没打线段树,这回居然一次过了... 感觉数论方面应该已经没有太大问题了... 之后要开始搞动态规划之类的东西了... 题意 在一片美丽的大陆上有100000个国家,记为1到100000.这里经济发达,有数不尽的账房,并且每个国家有一个银行.某大公司的领袖在这100000个银行开户时都存了3大洋,他惜财如命,因此会不时地派小弟GFS清点一些银行的存款或者让GFS改变某个银行的存款.该村子在财产上的求和运算等同于我们的乘法运算,也就是说领袖开户时的存款总

HFUUOJ1024 动态开点线段树+标记永久化

题意 分析 动态加点线段树,标记永久化好写常数小 Code #include<bits/stdc++.h> #define fi first #define se second #define lson l,mid,p<<1 #define rson mid+1,r,p<<1|1 #define pb push_back #define ll long long using namespace std; const ll inf=1e18; const int mod=

zoj 3573 Under Attack(线段树 标记法 最大覆盖数)

Under Attack Time Limit:  10 Seconds      Memory Limit:  65536 KB Doctor serves at a military air force base. One day, the enemy launch a sudden attack and the base is under heavy fire. The fighters in the airport must take off to intercept enemy bom