【P3373】 【模板】线段树 2 {线段树,模板}

OMG_Data_Structure So_Interesting_Mother-Fucker(译:数据结构,奥妙重重)

虽然只是模板,但还是挺麻烦的,可见数据结构都是毒瘤。

已知一个数列,你需要进行下面三种操作:

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

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

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

首先一定要用lazy标记,不然妥妥TLE。

这道题比最基本的模板多了乘法和取模,但加法和乘法这两个玩意和取模是很友好的,所以很简单。

通过思考加法和乘法的运算律,可以写出如下代码。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #define repo(i,a,b) for(int i = a;i <= b;++i)
  5 #define repi(i,a,b) for(int i = a;i >= b;++i)
  6 using namespace std;
  7
  8 struct segment_tree{
  9     int l,r;
 10     long long sum,add,mul;//sum为区间和,add为加法懒惰标记,mul为乘法懒惰标记
 11     #define l(x) tree[x].l
 12     #define r(x) tree[x].r
 13     #define sum(x) tree[x].sum
 14     #define add(x) tree[x].add
 15     #define mul(x) tree[x].mul//节省寿命防止眼瞎的宏定义
 16 } tree[100002*4];
 17 long long a[100002],n,m,mod,oper,x,y,k;//oper表示操作编号
 18
 19 inline int read(){//快读
 20     long long s=0,w=1;
 21     char ch = getchar();
 22     while(ch < ‘0‘ || ch > ‘9‘){
 23         if(ch == ‘-‘)w = -1;
 24         ch = getchar();
 25     }
 26     while(ch >= ‘0‘ && ch <= ‘9‘){
 27         s = s*10+ch-‘0‘;
 28         ch = getchar();
 29     }
 30     return w*s;
 31 }
 32
 33 inline void build(int p,int l,int r){//建树
 34     l(p) = l,r(p) = r;
 35     mul(p) = 1;//别忘了初始化mul(p),C++11才支持struct内non_static变量的初始化
 36     if(l==r){sum(p) = a[l] % mod;return;}
 37     int mid = (l+r)/2;
 38     build(p*2,l,mid);
 39     build(p*2+1,mid+1,r);
 40     sum(p) = (sum(p*2) + sum(p*2+1)) % mod;
 41 }
 42
 43 inline int len(int p){//节省寿命防止眼瞎的函数
 44     return r(p)-l(p)+1;
 45 }
 46
 47 inline void spread(int p){
 48     if(add(p) || mul(p)!=1){//如果有标记要传的话
 49         sum(p*2) = sum(p*2)*mul(p) % mod;
 50         sum(p*2+1) = sum(p*2+1)*mul(p) % mod;
 51         mul(p*2) = mul(p*2)*mul(p) % mod;
 52         mul(p*2+1) = mul(p*2+1)*mul(p) % mod;
 53         add(p*2) = add(p*2)*mul(p) % mod;
 54         add(p*2+1) = add(p*2+1)*mul(p) % mod;//子树的add也会受到乘法的影响,即(a+add)*mul
 55         mul(p) = 1;//mul(p) down
 56
 57         sum(p*2) = (sum(p*2)+add(p)*len(p*2)) % mod;
 58         sum(p*2+1) = (sum(p*2+1)+add(p)*len(p*2+1)) % mod;
 59         add(p*2) = (add(p*2)+add(p)) % mod;
 60         add(p*2+1) = (add(p*2+1)+add(p)) % mod;
 61         add(p) = 0;//add(p) down
 62     }
 63 }//除了把p*2和p*2+1各自都写出来,还可以写一个devide_spread()函数
 64  //调用devide_spread(p*2),devide_spread(p*2+1)
 65
 66 inline void oper_add(int p,int l,int r,int d){//加法操作
 67     if(l <= l(p) && r >= r(p)){
 68         sum(p) = (sum(p)+(long long)d*len(p)) % mod;
 69         add(p) = (add(p)+d) % mod;
 70         return;
 71     }
 72     spread(p);
 73     int mid = (l(p)+r(p))/2;
 74     if(l <= mid)oper_add(p*2,l,r,d);
 75     if(r > mid)oper_add(p*2+1,l,r,d);
 76     sum(p) = (sum(p*2)+sum(p*2+1)) % mod;
 77 }
 78
 79 inline void oper_mul(int p,int l,int r,int d){//乘法操作
 80     if(l <= l(p) && r >= r(p)){
 81         sum(p) = (sum(p)*d) % mod;
 82         add(p) = (add(p)*d) % mod;//同spread()函数里的解释
 83         mul(p) = (mul(p)*d) % mod;
 84         return;
 85     }
 86     spread(p);
 87     int mid = (l(p)+r(p))/2;
 88     if(l <= mid)oper_mul(p*2,l,r,d);
 89     if(r > mid)oper_mul(p*2+1,l,r,d);
 90     sum(p) = (sum(p*2)+sum(p*2+1)) % mod;
 91 }
 92
 93 inline long long ask(int p,int l,int r){//询问
 94     if(l <= l(p) && r >= r(p))return sum(p) % mod;
 95     spread(p);
 96     int mid = (l(p)+r(p))/2;
 97     long long val = 0;
 98     if(l <= mid)val = (val+ask(p*2,l,r)) % mod;
 99     if(r > mid)val = (val+ask(p*2+1,l,r)) % mod;
100     return val;
101 }
102
103 int main(){
104     n = read(),m = read(),mod = read();
105     repo(i,1,n){
106         a[i] = read();
107     }
108     build(1,1,n);
109     repo(i,1,m){
110         oper = read();
111         if(oper == 1){
112             x = read(),y = read(),k = read();
113             oper_mul(1,x,y,k);
114         }
115         if(oper == 2){
116             x = read(),y = read(),k = read();
117             oper_add(1,x,y,k);
118         }
119         if(oper == 3){
120             x = read(),y = read();
121             printf("%lld\n",ask(1,x,y));
122         }
123     }
124     return 0;
125 }

原文地址:https://www.cnblogs.com/c-come/p/10349285.html

时间: 2024-11-01 15:45:54

【P3373】 【模板】线段树 2 {线段树,模板}的相关文章

线段树详解及模板 (转载)

一步一步理解线段树 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)

吊打线段树的超级树状数组

你是否讨厌线段树那冗长的代码?你是否还在因为线段树的难调试而满头♂dark汗?那么,请不要错过!超级树状数组特价!只要998,只要998! ##¥……#……¥%……&%¥……ER#%$#$#^T%$^$% 超级树状数组,其实是一种能够支持区间修改和区间查询的树状数组,和线段树相比,它的常数极小,不需要太多空间,代码量也少了很多(简直吊打线段树) 1.树状数组 既然是超级树状数组,那么就需要一个树状数组作为基础了.但是在真正实现时,只用到了lowbit()函数(所以说lowbit是树状数组的核心啊

[可持久化线段树(主席树)]

主席树 抛出问题 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个整数,表示这个序列各项的数字. 接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值. 输出格式: 输出包含k行,每行1个整数,依次表示每一次查询的结果 解决问题 主席树(可持久化线段树)法 于是针对这个问题,新的数据结构诞生了,也就是主席树. 主席树本名

知识点 - 线段树 权值 树套树 二维 可持续

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 inline int ls(int p) { return p << 1; }//左儿子 inline int rs(int p) { return p << 1 | 1; }//右儿子 void push_up(int p) { t[p] = t[ls(p)] + t[rs(p)]; }// 向上不断维护区间操作 void build(ll p, ll l, ll r) { if (l == r) { t[p] =

K-th Number 线段树(归并树)+二分查找

K-th Number 题意:给定一个包含n个不同数的数列a1, a2, ..., an 和m个三元组表示的查询.对于每个查询(i, j, k), 输出ai, ai+1, ... ,aj的升序排列中第k个数 . 题解:用线段树,每个节点维护一个区间并且保证内部升序,对于每次查询x,返回该区间小于x的数的个数.就这样不断二分,直到找到x为止. 线段树(归并树)+二分查找 1 #include <iostream> 2 #include <cstdio> 3 #include <

BZOJ 3065 带插入区间K小值 替罪羊树套线段树

题目大意:带插入,单点修改的区间k小值在线查询. 思路:本年度做过最酸爽的题. 树套树的本质是一个外层不会动的树来套一个内层会动(或不会动)的树.两个树的时间复杂度相乘也就是差不多O(nlog^2n)左右.但是众所周知,高级数据结构经常会伴有庞大的常数,所以一般来说树套树的常数也不会小到哪去.所以在做这种题的时候先不要考虑常数的问题... 为什么要用替罪羊树呢?因为一般的平衡树都是会动的,这就很难办了.外层的树动了之后,内层的树肯定也是会动的.很显然,一般的二叉平衡树会经常会旋转,这样在动外层的

HDOJ1556 Color the ball 【线段树】+【树状数组】+【标记法】

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8272    Accepted Submission(s): 4239 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

笔试算法题(42):线段树(区间树,Interval Tree)

议题:线段树(Interval Tree) 分析: 线段树是一种二叉搜索树,将一个大区间划分成单元区间,每个单元区间对应一个叶子节点:内部节点对应部分区间,如对于一个内部节点[a, b]而言,其左子节点表示的区间为[a, (a+b)/2],其右子节点表示的区间为[1+(a+b)/2, b]: 对于区间长度为N的线段树,由于其单元节点都是[a, a]的叶子节点,所以其叶子节点数为N,并且整棵树为平衡二叉树,所以总节点数为2N-1,树的深度为log(N)+1: 插入操作:将一条线段[a, b]插入到

HDU 5877 dfs+ 线段树(或+树状树组)

1.HDU 5877  Weak Pair 2.总结:有多种做法,这里写了dfs+线段树(或+树状树组),还可用主席树或平衡树,但还不会这两个 3.思路:利用dfs遍历子节点,同时对于每个子节点au,查询它有多少个祖先av满足av<=k/au. (1)dfs+线段树 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm>