luogu3373 【模板】线段树2
题目大意:
已知一个数列,你需要进行下面三种操作:
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的更多相关文章
- 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 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询
点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...
- 【线段树】【P3372】模板-线段树
百度百科 Definition&Solution 线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题.期望情况下,复杂度为O(nlogn). 核心思想见百度百科,线段树即将每个 ...
- 算法模板——线段树6(二维线段树:区域加法+区域求和)(求助phile)
实现功能——对于一个N×M的方格,1:输入一个区域,将此区域全部值作加法:2:输入一个区域,求此区域全部值的和 其实和一维线段树同理,只是不知道为什么速度比想象的慢那么多,求解释...@acphile ...
- 【洛谷 p3373】模板-线段树 2(数据结构--线段树)
题意:已知一个数列,你需要进行下面三种操作:1.将某区间每一个数加上x:2.将某区间每一个数乘上x:3.求出某区间每一个数的和. 解法:(唉 :-(,这题卡住我了......)对于加法和乘法的混合操作 ...
- 【洛谷 p3372】模板-线段树 1(数据结构--线段树)
题目:已知一个数列,你需要进行下面两种操作:1.将某区间每一个数加上x:2.求出某区间每一个数的和. 解法:如题,模版题.需要加上 lazy 标记,也就是我的 upd.lazy 标记的思路就是对一个结 ...
- 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 ...
- poj 3468 A Simple Problem with Integers 线段树 题解《挑战程序设计竞赛》
地址 http://poj.org/problem?id=3468 线段树模板 要背下此模板 线段树 #include <iostream> #include <vector> ...
- 线段树 扫描线 L - Atlantis HDU - 1542 M - City Horizon POJ - 3277 N - Paint the Wall HDU - 1543
学习博客推荐——线段树+扫描线(有关扫描线的理解) 我觉得要注意的几点 1 我的模板线段树的叶子节点存的都是 x[L]~x[L+1] 2 如果没有必要这个lazy 标志是可以不下传的 也就省了一个pu ...
随机推荐
- Min(BZOJ 1441)
题目描述 给出n个数(A1...An)现求一组整数序列(X1...Xn)使得S=A1*X1+...An*Xn>0,且S的值最小 输入 第一行给出数字N,代表有N个数 下面一行给出N个数 输出 S ...
- SHUoj 神无月排位赛
神无月排位赛 发布时间: 2017年7月8日 21:06 最后更新: 2017年7月8日 22:35 时间限制: 1000ms 内存限制: 128M 描述 <神无月>作为盛大游 ...
- 封装的一个Worker方法,利用线程处理运行时间较长的js代码
function webWorker(targetFile,content){ var promise=new Promise(function(resolve,reject){ var web=ne ...
- RRAS
远程访问控制是Windows NT.Win200x Server提供的一种远程服务,它允许用户从远端通过拨号连接连接到一个本地的计算机网络,一旦建立了连接,就相当于处在了本地的LAN中,从而可以使用各 ...
- 标准C程序设计七---03
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- python--爬取http://www.kuaidaili.com/并保存为xls
代码如下: 复制在python3上先试试吧^_^ # -*- coding: utf-8 -*- """ Created on Mon Jun 12 13:27:59 2 ...
- R语言入门视频笔记--1
一.数据框简要 可输入来访问mtcars这个系统自带的数据框中的mpg列 mtcars$mpg 或者输入 mtcars[c("mpg","cyl")] 来访问两 ...
- [OS X实用技巧]机器人应用:一键将图片转换为PNG/JPEG/TIFF
转自:http://www.maczhi.com/archives/2842.html 按教程老出错....试验了后使用: - 取得指定Finder对象,其它不变,但运行后不会出错. OS X实用技巧 ...
- 计算机windows7连接打印机
计算机连接打印机 (1)查看打印机的名字. 示例,打印机名为 HP LaserJet M1522 .... (2)打开windows开始菜单,点击[设备和打印机],然后查看打印机和传真那个区域是否有打 ...
- SLAVEOF以后
当我们想要某个Redis服务器复制另一个服务器时,我们可以在连接这个Redis服务器的客户端上输入“SLAVEOF”命令指定另一个服务器的IP地址和端口号: SLAVEOF <master_ip ...