题目大意:

已知一个数列,你需要进行下面三种操作:
1.将某区间每一个数乘上x
2.将某区间每一个数加上x
3.求出某区间每一个数的和

本线段树的标记是个二元组:add和mul,其代表将一个线段中的每一个点乘以mul再加add。设区间长度为x,原来区间和为sum。如果两个标记要叠加,标记叠加前区间上的和将是sum*mul+add,叠加后的值将是(sum*mul+add)*mul'+add'=mul*mul'*sum+add*mul'+add'。所以将mul*=mul', add=add*mul'+add'即可。

注意:

  • 尽管数据关于P取模了,但是因为有数据相乘的操作,所以程序中所有的值类型都要是long long
  • 宏定义ModPlus, ModMult时,如ModMult,不要写成((x%P)*(y%P))%P,应该写成(x*y)%P,否则就被卡常数了。
#include <cstdio>
#include <cstring>
#include <cassert>
using namespace std; const int MAX_RANGE=100010, MAX_NODE = MAX_RANGE * 4;
#define LOOP(i, n) for(int i=1; i<=n; i++)
long long P, TotRange;
long long OrgData[MAX_RANGE]; struct RangeTree
{
private:
#define ModPlus(x, y) ((x)%P+(y)%P)%P
#define ModMult(x, y) ((x)%P*(y)%P)%P
#define lSon cur*2, l, mid
#define rSon cur*2+1, mid+1, r
#define Lson cur*2, sl, mid, al, ar
#define Rson cur*2+1, mid+1, sr, al, ar struct Tag
{
long long add, mul;
Tag() {}
Tag(int m, int a):mul(m),add(a){}
void Refresh(Tag x) { mul = ModMult(mul, x.mul); add = ModMult(add, x.mul); add = ModPlus(add, x.add); }
void Clear() { add = 0; mul = 1; }
int GetSum(int sum, int l, int r) { return ModPlus(ModMult(sum, mul), ModMult(add, (r - l + 1))); }
};
Tag _tags[MAX_NODE];
long long Sum[MAX_NODE]; void PushDown(int cur, int l, int r)
{
if (_tags[cur].add != 0 || _tags[cur].mul != 1)
{
int mid = (l + r) / 2;
Sum[cur * 2] = _tags[cur].GetSum(Sum[cur * 2], l, mid);
Sum[cur * 2 + 1] = _tags[cur].GetSum(Sum[cur * 2 + 1], mid + 1, r);
_tags[cur * 2].Refresh(_tags[cur]);
_tags[cur * 2 + 1].Refresh(_tags[cur]);
_tags[cur].Clear();
}
} void PullUp(int cur)
{
Sum[cur] = ModPlus(Sum[cur * 2], Sum[cur * 2 + 1]);
} void Update(int cur, int sl, int sr, int al, int ar, int op, int value)
{
assert(al <= ar && sl <= sr && al <= sr && ar >= sl);
if (al <= sl && sr <= ar)
{
if (op == 1)
{
Sum[cur] = ModMult(Sum[cur], value);
_tags[cur].Refresh(Tag(value, 0));
}
else if (op == 2)
{
Sum[cur] = ModPlus(Sum[cur], (sr - sl + 1)*value);
_tags[cur].Refresh(Tag(1, value));
}
return;
}
PushDown(cur, sl, sr);
int mid = (sl + sr) / 2;
if (al <= mid)
Update(Lson, op, value);
if (ar > mid)
Update(Rson, op, value);
PullUp(cur);
} int Query(int cur, int sl, int sr, int al, int ar)
{
assert(al <= ar && sl <= sr && al <= sr && ar >= sl);
if (al <= sl && sr <= ar)
return Sum[cur];
PushDown(cur, sl, sr);
int mid = (sl + sr) / 2, ans = 0;
if (al <= mid)
ans = ModPlus(ans, Query(Lson));
if (ar > mid)
ans = ModPlus(ans, Query(Rson));
PullUp(cur);
return ans;
} void SetEachNode(long long *a, int cur, int l, int r)
{
_tags[cur] = Tag(1, 0);
if (l == r)
{
Sum[cur] = a[l];
return;
}
int mid = (l + r) / 2;
SetEachNode(a, lSon);
SetEachNode(a, rSon);
PullUp(cur);
} public:
RangeTree() {} void SetEachNode(long long *a)
{
SetEachNode(a, 1, 1, TotRange);
} void Update(int l, int r, int op, int value)
{
Update(1, 1, TotRange, l, r, op, value);
} long long Query(int l, int r)
{
return Query(1, 1, TotRange, l, r);
}
}g; int main()
{
int opCnt, op, l, r, val;
scanf("%lld%d%lld", &TotRange, &opCnt, &P);
LOOP(i, TotRange)
scanf("%lld", OrgData + i);
g.SetEachNode(OrgData);
while (opCnt--)
{
scanf("%d", &op);
switch (op)
{
case 1://Mult
scanf("%d%d%d", &l, &r, &val);
g.Update(l, r, 1, val);
break;
case 2://Plus
scanf("%d%d%d", &l, &r, &val);
g.Update(l, r, 2, val);
break;
case 3://Query
scanf("%d%d", &l, &r);
printf("%lld\n", g.Query(l, r));
break;
}
}
return 0;
}

  

luogu3373 【模板】线段树2的更多相关文章

  1. hdu 1754 I Hate It (模板线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others)    M ...

  2. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  3. hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询

    点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...

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

    百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个 ...

  5. 算法模板——线段树6(二维线段树:区域加法+区域求和)(求助phile)

    实现功能——对于一个N×M的方格,1:输入一个区域,将此区域全部值作加法:2:输入一个区域,求此区域全部值的和 其实和一维线段树同理,只是不知道为什么速度比想象的慢那么多,求解释...@acphile ...

  6. 【洛谷 p3373】模板-线段树 2(数据结构--线段树)

    题意:已知一个数列,你需要进行下面三种操作:1.将某区间每一个数加上x:2.将某区间每一个数乘上x:3.求出某区间每一个数的和. 解法:(唉 :-(,这题卡住我了......)对于加法和乘法的混合操作 ...

  7. 【洛谷 p3372】模板-线段树 1(数据结构--线段树)

    题目:已知一个数列,你需要进行下面两种操作:1.将某区间每一个数加上x:2.求出某区间每一个数的和. 解法:如题,模版题.需要加上 lazy 标记,也就是我的 upd.lazy 标记的思路就是对一个结 ...

  8. hdu 1754 I Hate It (线段树、单点更新)(PS:ios::sync_with_stdio(false)可以加快cin、cout的读取写出速度)

    I Hate ItTime Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  9. poj 3468 A Simple Problem with Integers 线段树 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=3468 线段树模板 要背下此模板 线段树 #include <iostream> #include <vector> ...

  10. 线段树 扫描线 L - Atlantis HDU - 1542 M - City Horizon POJ - 3277 N - Paint the Wall HDU - 1543

    学习博客推荐——线段树+扫描线(有关扫描线的理解) 我觉得要注意的几点 1 我的模板线段树的叶子节点存的都是 x[L]~x[L+1] 2 如果没有必要这个lazy 标志是可以不下传的 也就省了一个pu ...

随机推荐

  1. Spring-IOC源码解读2.3-BeanDefinition的注册

    在DefaultListAbleBeanFactory中通过一个HashMap持有载入的BeanDefinition信息 ,这个HashMap的定义在DefaultListAbleBeanFactor ...

  2. 16.1112 模拟考试 T1

    加密[问题描述]有一种不讲道理的加密方法是: 在字符串的任意位置随机插入字符. 相应的,不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符.给定原文s和加密后的字符串t,求?有多少子串可以通过 ...

  3. msp430项目编程54

    msp430综合项目---扩展项目四54 1.电路工作原理 2.代码(显示部分) 3.代码(功能实现) 4.项目总结

  4. 结构字段验证--validator.v9

    官网:https://godoc.org/gopkg.in/go-playground/validator.v9#hdr-Baked_In_Validators_and_Tags package va ...

  5. (46)C#注册表及读写

    启动注册表:regedit 结构: 注册表一共有7个配置单元用regedit只能看到5个 HKEY_CLASSES_ROOT 包含系统上文件类型的细节(.txt,.doc)等.以及使用那些应用程序可以 ...

  6. 洛谷 P3865 【模板】ST表

    P3865 [模板]ST表 题目背景 这是一道ST表经典题——静态区间最大值 请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)O(1) 题目描述 给定一个长度为  ...

  7. 实现uitable cell中点击button设置当前cell为选中状态

    - (void)buttonClick:(id)senser{    NSInteger tag = [senser tag];    NSLog(@"the button tag is % ...

  8. Android Studio 1.3RC版 build加速

    Android Studio 确实是好用.但build的速度却是奇慢无比!.! ! 我上网找了非常多build加速的设置,却不能适配到我的1.3RC版... . .心塞.无耐,忍着超级无敌慢的速度硬是 ...

  9. validation set以及cross validation的常见做法

    如果给定的样本充足,进行模型选择的一种简单方法是随机地将数据集切分成三部分,分为训练集(training set).验证集(validation set)和测试集(testing set).训练集用来 ...

  10. vim列块操作

    一.可视模式 进入可视模式有三种方法:v,V,CTRL+V (1)按v启用可视模式,能够按单个字符选择内容,移动光标能够选择. 如: (2)按V启用可视模式,立马选中光标所在行.按单行符选择内容.移动 ...