[洛谷3373]【模板】线段树 2

思路:

线段树。同时维护两个 lazy tag ,一个维护乘,一个维护加。
根据加法结合律,可以得出:当同一个结点进行两次加操作时,新的标记等于两次标记之和。
根据乘法结合律,可以得出:当同一个结点进行两次乘操作时,新的标记等于两次标记之积。
根据乘法分配律,可以得出:当同一个结点先进行了加操作,再进行乘操作时,两个标记都要乘以新乘上的值。

  1 #include<cstdio>
  2 #include<cctype>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 using namespace std;
  7 #define ll long long
  8 #define maxn 100001
  9 #define root 1
 10 #define _left <<1
 11 #define _right <<1|1
 12 int n,m;
 13 ll mod;
 14 inline int getint() {
 15     char ch;
 16     while(!isdigit(ch=getchar()));
 17     int x=ch^‘0‘;
 18     while(isdigit(ch=getchar())) x=((x+(x<<2))<<1)+(ch^‘0‘);
 19     return x;
 20 }
 21 inline ll getll() {
 22     char ch;
 23     while(!isdigit(ch=getchar()));
 24     ll x=ch^‘0‘;
 25     while(isdigit(ch=getchar())) x=((x+(x<<2))<<1)+(ch^‘0‘);
 26     return x;
 27 }
 28 struct SegmentTree {
 29     ll val[maxn<<2],add[maxn<<2],mul[maxn<<2];
 30     void push_up(const int p) {
 31         val[p]=(val[p _left]+val[p _right])%mod;
 32     }
 33     void build(const int p,const int b,const int e) {
 34         mul[p]=1;
 35         add[p]=0;
 36         if(b==e) {
 37             val[p]=getll()%mod;
 38             return;
 39         }
 40         int mid=(b+e)>>1;
 41         build(p _left,b,mid);
 42         build(p _right,mid+1,e);
 43         push_up(p);
 44     }
 45     int length(const int b,const int e) {
 46         return e-b+1;
 47     }
 48     void push_down(const int p,const int b,const int e) {
 49         if(mul[p]!=1) {
 50             val[p _left]=val[p _left]*mul[p]%mod;
 51             val[p _right]=val[p _right]*mul[p]%mod;
 52             mul[p _left]=mul[p _left]*mul[p]%mod;
 53             mul[p _right]=mul[p _right]*mul[p]%mod;
 54             add[p _left]=add[p _left]*mul[p]%mod;
 55             add[p _right]=add[p _right]*mul[p]%mod;
 56             mul[p]=1;
 57         }
 58         if(add[p]) {
 59             int mid=(b+e)>>1;
 60             val[p _left]=(val[p _left]+add[p]*length(b,mid))%mod;
 61             val[p _right]=(val[p _right]+add[p]*length(mid+1,e))%mod;
 62             add[p _left]=(add[p _left]+add[p])%mod;
 63             add[p _right]=(add[p _right]+add[p])%mod;
 64             add[p]=0;
 65         }
 66     }
 67     void modify_mul(const int p,const int b,const int e,const int l,const int r,const ll x) {
 68         if((b==l)&&(e==r)) {
 69             val[p]=val[p]*x%mod;
 70             mul[p]=mul[p]*x%mod;
 71             add[p]=add[p]*x%mod;
 72             return;
 73         }
 74         push_down(p,b,e);
 75         int mid=(b+e)>>1;
 76         if(l<=mid) modify_mul(p _left,b,mid,l,min(mid,r),x);
 77         if(r>mid) modify_mul(p _right,mid+1,e,max(mid+1,l),r,x);
 78         push_up(p);
 79     }
 80     void modify_add(const int p,const int b,const int e,const int l,const int r,const ll x) {
 81         if((b==l)&&(e==r)) {
 82             val[p]=(val[p]+x*length(b,e))%mod;
 83             add[p]=(add[p]+x)%mod;
 84             return;
 85         }
 86         push_down(p,b,e);
 87         int mid=(b+e)>>1;
 88         if(l<=mid) modify_add(p _left,b,mid,l,min(mid,r),x);
 89         if(r>mid) modify_add(p _right,mid+1,e,max(mid+1,l),r,x);
 90         push_up(p);
 91     }
 92     ll query(const int p,const int b,const int e,const int l,const int r) {
 93         if((b==l)&&(e==r)) {
 94             return val[p];
 95         }
 96         int mid=(b+e)>>1;
 97         ll ans=0;
 98         push_down(p,b,e);
 99         if(l<=mid) ans=(ans+query(p _left,b,mid,l,min(mid,r)))%mod;
100         if(r>mid) ans=(ans+query(p _right,mid+1,e,max(mid+1,l),r))%mod;
101         return ans;
102     }
103 };
104 SegmentTree tree;
105 int main() {
106     n=getint();
107     m=getint();
108     mod=getll();
109     tree.build(root,1,n);
110     while(m--) {
111         int op=getint(),x=getint(),y=getint();
112         if(op==1) {
113             ll k=getll()%mod;
114             tree.modify_mul(root,1,n,x,y,k);
115             continue;
116         }
117         if(op==2) {
118             ll k=getll()%mod;
119             tree.modify_add(root,1,n,x,y,k);
120             continue;
121         }
122         printf("%lld\n",tree.query(root,1,n,x,y));
123     }
124     return 0;
125 }
时间: 2024-08-07 09:15:38

[洛谷3373]【模板】线段树 2的相关文章

线段树_区间加乘(洛谷P3373模板)

题目描述 如题,已知一个数列,你需要进行下面三种操作: 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

AC自动机(附洛谷P3769模板题)

首先,介绍一下AC自动机(Aho-Corasick automaton),是一种在一个文本串中寻找每一个已给出的模式串的高效算法. 在学习AC自动机之前,你需要先学习Trie树和KMP算法,因为AC自动机正式利用并结合了两者的思想. 说到实际的不同,其实AC自动机只是在Trie树上引入了一个类似KMP中next数组的东西叫做Fail指针. 对于每一个节点,Fail指针指向该节点所代表的字符串中,次长的.在Trie树中存在的后缀(因为最长的在Trie树种存在的后缀就是其本身)所代表的节点. 举例:

算法模板——线段树1(区间加法+区间求和)

实现功能——1:区间加法:2:区间求和 最基础最经典的线段树模板.由于这里面操作无顺序之分,所以不需要向下pushup,直接累积即可 1 var 2 i,j,k,l,m,n,a1,a2,a3,a4:longint; 3 a,b:array[0..100000] of longint; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min

算法模板——线段树5(区间开根+区间求和)

实现功能——1:区间开根:2:区间求和(此模板以BZOJ3038为例) 作为一个非常规的线段树操作,其tag也比较特殊呵呵哒 1 var 2 i,j,k,l,m,n:longint; 3 a,b:array[0..500000] of int64; 4 function max(x,y:longint):longint;inline; 5 begin 6 if x>y then max:=x else max:=y; 7 end; 8 function min(x,y:longint):long

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

【线段树】【P3372】模板-线段树

百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个线段分成左右两个线段做左右子树.一个线段没有子树,当且仅当线段表示的区间为[a,a]. 由于编号为k的节点的子节点为2k以及2k+1,线段树可以快速的递归左右叶节点. lazy标记:当进行区间修改的时候,如果一个区间整体全部被包含于要修改的区间,则可以将该区间的值修改后,将lazy标记打在区间上,不再递归左

CSU-ACM集训-模板-线段树进阶

A题 原CF 438D The Child and Sequence 题意 给一串数字,m次操作,1.区间查询:2.区间取模:3.单点修改 基本思路 考虑到模如果大于区间的最大值,则取模没有意义.若小于则向下查询并修改,考虑到一个数每次取模最多为原数的\(1/2\),则可认为修改次数不超过\(\log{2}{n}\) 时间复杂度为\(O(n\log{2}{n}\log{2}{n})\) #include<bits/stdc++.h> #define FOR(i,a,b) for(int i=a

模板 - 线段树

线段树还需要模板的菜鸡 #include<bits/stdc++.h> using namespace std; typedef long long ll; #define lt ls, l, m #define rt rs, m + 1, r #define ls (o<<1) #define rs (o<<1|1) const int MAXM = 100000 + 5; ll a[MAXM]; ll st[MAXM * 4], lazy[MAXM * 4]; in

算法模板——线段树4(区间加+区间乘+区间覆盖值+区间求和)

实现功能——1:区间加法 2:区间乘法 3:区间覆盖值 4:区间求和 这是个四种常见线段树功能的集合版哦...么么哒(其实只要协调好三种tag的关系并不算太难——前提是想明白了线段树的工作模式) 代码长度几经修改后也大为缩水 还有!!!——通过BZOJ1798反复的尝试,我的出来一个重要结论——尽量减少pushup操作的不必要使用次数,对于程序提速有明显的效果!!! 1 type vet=record 2 a0,a1:longint; 3 end; 4 var 5 i,j,k,l,m,n,a1,