线段树の二 区间乘+区间加

具体就不解释了,看上一篇文章

放代码

注意点:!!!!

注意运算符优先级

比如:

a*=b%p 是b先mod p再与a相乘

参见:https://baike.baidu.com/item/%E8%BF%90%E7%AE%97%E7%AC%A6%E4%BC%98%E5%85%88%E7%BA%A7/4752611?fr=aladdin

/*******************************
线段树V2.0
支持区间加、区间乘、区间和查询
********************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
using namespace std;
struct node
{
	int left;//节点所代表区间左端
	int right;//节点所代表区间右端
	long long sum;//区间和
	long long add;//区间加Lazy标记
	long long mult;//区间乘Lazy标记
	node(){mult=1;}
} tree[N];
long long a[N];
int n,m,p;//n:区间大小 m:操作次数 p:取模 

//left:当前区间左端 right:当前区间右端 node:当前节点
void Build_Tree(int left,int right,int node)
{
	tree[node].left=left;
	tree[node].right=right;
	if(left==right) tree[node].sum=a[left];
	else
	{
		int mid=(left+right)>>1;
		Build_Tree(left,mid,node<<1);
		Build_Tree(mid+1,right,node<<1|1);
		tree[node].sum=(tree[node<<1].sum+tree[node<<1|1].sum)%p;
	}
}

//标记下放 node:当前节点
void Push_Down(int node)
{
	if(tree[node].add==0&&tree[node].mult==1) return;
	tree[node<<1].mult=tree[node<<1].mult*tree[node].mult%p;
	tree[node<<1|1].mult=tree[node<<1|1].mult*tree[node].mult%p;
	tree[node<<1].add=tree[node<<1].add*tree[node].mult%p;
	tree[node<<1|1].add=tree[node<<1|1].add*tree[node].mult%p;
	tree[node<<1].sum=tree[node<<1].sum*tree[node].mult%p;
	tree[node<<1|1].sum=tree[node<<1|1].sum*tree[node].mult%p;
	tree[node].mult=1;
	tree[node<<1].add=(tree[node<<1].add+tree[node].add)%p;
	tree[node<<1|1].add=(tree[node<<1|1].add+tree[node].add)%p;
	tree[node<<1].sum=(tree[node<<1].sum+tree[node].add*(tree[node<<1].right-tree[node<<1].left+1))%p;
	tree[node<<1|1].sum=(tree[node<<1|1].sum+tree[node].add*(tree[node<<1|1].right-tree[node<<1|1].left+1))%p;
	tree[node].add=0;
}

//上推 node:当前节点
void Push_Up(int node)
{
	tree[node].sum=(tree[node<<1].sum+tree[node<<1|1].sum)%p;
}

//区间加操作 left:操作区间左端点 right:操作区间右端点 node:当前节点 value:操作值
void Add_Range(int left,int right,int node,long long value)
{
	if(tree[node].left>=left&&tree[node].right<=right)
	{
		tree[node].add+=value%p;
		tree[node].sum+=value*(tree[node].right-tree[node].left+1)%p;
		return;
	}
	Push_Down(node);
	int mid=(tree[node].left+tree[node].right)>>1;
	if(left<=mid) Add_Range(left,right,node<<1,value);
	if(right>mid) Add_Range(left,right,node<<1|1,value);
	Push_Up(node);
}

//区间乘操作 left:操作区间左端点 right:操作区间右端点 node:当前节点 value:操作值
void Mult_Range(int left,int right,int node,long long value)
{
	if(tree[node].left>=left&&tree[node].right<=right)
	{
		tree[node].mult=tree[node].mult*value%p;
		tree[node].add=tree[node].add*value%p;
		tree[node].sum=tree[node].sum*value%p;
		return;
	}
	Push_Down(node);
	int mid=(tree[node].left+tree[node].right)>>1;
	if(left<=mid) Mult_Range(left,right,node<<1,value);
	if(right>mid) Mult_Range(left,right,node<<1|1,value);
	Push_Up(node);
}

//区间和查询 left:查询区间左端点 right:查询区间右端点 node:当前节点
long long Query_Sum(int left,int right,int node)
{
	if(tree[node].left>right||tree[node].right<left)
	return 0;
	Push_Down(node);
	if(tree[node].left>=left&&tree[node].right<=right)
	return tree[node].sum%p;
	return (Query_Sum(left,right,node*2)+Query_Sum(left,right,node*2+1))%p;
}

int main()
{

	cin>>n>>m>>p;
	for(int i=1; i<=n; i++) cin>>a[i];
	Build_Tree(1,n,1);
	while(m--)
	{
		int x,y,c;
		cin>>c>>x>>y;
		if(c==1)//区间乘操作
		{
			long long v;
			cin>>v;
			Mult_Range(x,y,1,v);
		}
		if(c==2)//区间加操作
		{
			long long v;
			cin>>v;
			Add_Range(x,y,1,v);
		}
		if(c==3)//查询
		cout<<Query_Sum(x,y,1)<<endl;
	}
	return 0;
}

  

时间: 2024-10-28 11:04:34

线段树の二 区间乘+区间加的相关文章

线段树第二弹(区间更新)

上篇文章,我们介绍了线段树的基本概念和单点更新.区间查询,今天,我们来接着上次的线段树问题继续深入研究.在解决线段树问题的过程中,我们会遇到要求修改区间中某一元素值的问题,当然也可能会遇到要求修改一段子区间所有值的问题--即区间更新问题.回忆一下上篇文章单点更新的方法是,由叶节点逐级向上进行更新,此时更新一个节点值的时间复杂度为o(log n),(点击链接了解详情:线段树+RMQ问题第二弹),那么以这样的处理效率来进行区间更新结果会怎样?现在假设待更新区间数据的规模为 n ,那么就需要进行 n

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

hdu 1754 I Hate It 线段树单点更新和区间求和

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 参照HH大牛写的额! Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input 本题目包含多

hdu4027 Can you answer these queries?(线段树平方减少,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the bat

hdoj 2795 Billboard 【线段树 单点更新 + 维护区间最大值】

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 15719    Accepted Submission(s): 6629 Problem Description At the entrance to the university, there is a huge rectangular billboard of

hdu 1166 敌兵布阵(线段树之 单点更新+区间求和)

敌兵布阵                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵

HDU 4893 线段树的 点更新 区间求和

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2067    Accepted Submission(s): 619 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

poj 3468 线段树 成段增减 区间求和

题意:Q是询问区间和,C是在区间内每个节点加上一个值 Sample Input 10 51 2 3 4 5 6 7 8 9 10Q 4 4Q 1 10Q 2 4C 3 6 3Q 2 4Sample Output 455915 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <cmath> 6

约会安排---hdu4553(线段树,麻烦的区间覆盖)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4553 算是poj3667的加强版,建立两颗线段树,一个是DS区间,另一个是NS区间.那么根据题意,如果是DS的请求,那么首先查找DS的区间,如果有满足的区间就更新DS区间,NS的区间不需要更新.如果是NS的请求,首先看DS区间是否有满足的区间,否则查找NS区间,如果有就同时更新DS区间和NS区间.那么可以归纳为,只要是NS的请求,就同时更新两颗线段树,否则只更新DS的线段树. 注意输出要从Outpu