线段树的懒标记

上次看的那个视频讲线段树的时候压根没讲懒标记,然后我今天去写题目直接被薄纱!都是70分,剩下3个节点tml!!!

懒标记

我们在修改一些区间的时候,按照我昨天来学的来修改要改到最下面的叶节点去,可是这样子就没有了线段树的精华的区间修改,这样子我们时间复杂度会大大增加就像这样子

void updata(int arr[], int tree[], int node, int start, int end, int idx, int val)//位置 改变后的数
{
	if (start == end)
	{
		arr[idx] = val;
		tree[node] = val;
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2 + 1;
		int right_node = node * 2 + 2;
		if (idx >= start && idx <= mid)
		{
			updata(arr, tree, left_node, start, mid, idx, val);
		}
		else
		{
			updata(arr, tree, right_node, mid+1, end, idx, val);
		}
		tree[node] = tree[left_node] + tree[right_node];
	}
}

于是我们就引入了懒标记这个东西,简单的来说,就是我们如果是要修改6到9,那么我们可以直接在6到9这个地方加上一个lazy=我们要增加的值,然后我们如果在后面要运用这个节点下面的节点的时候,就把这个lazy下放,这样子我们就可以不需要一定要找到最下面的节点,大大节约了时间复杂度!

 然后就是我写的线段树的题目,不过只有70分(;´༎ຶД༎ຶ`) 

# 【模板】线段树 1

## 题目描述

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

1. 将某区间每一个数加上 $k$。
2. 求出某区间每一个数的和。

## 输入格式

第一行包含两个整数 $n, m$,分别表示该数列数字的个数和操作的总个数。

第二行包含 $n$ 个用空格分隔的整数,其中第 $i$ 个数字表示数列第 $i$ 项的初始值。

接下来 $m$ 行每行包含 $3$ 或 $4$ 个整数,表示一个操作,具体如下:

1. `1 x y k`:将区间 $[x, y]$ 内每个数加上 $k$。
2. `2 x y`:输出区间 $[x, y]$ 内每个数的和。

## 输出格式

输出包含若干行整数,即为所有操作 2 的结果。

## 样例 #1

### 样例输入 #1

```
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
```

### 样例输出 #1

```
11
8
20
```

## 提示

对于 $30\%$ 的数据:$n \le 8$,$m \le 10$。  
对于 $70\%$ 的数据:$n \le {10}^3$,$m \le {10}^4$。  
对于 $100\%$ 的数据:$1 \le n, m \le {10}^5$。

保证任意时刻数列中所有元素的绝对值之和 $\le {10}^{18}$。

**【样例解释】**

![](https://cdn.luogu.com.cn/upload/pic/2251.png)

#include <stdio.h>
void bulid_tree(long long arr[], long long tree[], int node, int start, int end)
{
	if (start == end)
	{
		tree[node] = arr[start];
	}
	else
	{
	    int mid = (start + end) / 2;
	    int left_node = node * 2;
	    int right_node = node * 2+1;
	    bulid_tree(arr, tree, left_node, start, mid);
	    bulid_tree(arr, tree, right_node, mid+1, end);
	    tree[node] = tree[left_node] + tree[right_node];
	}
}
long long qureytree(long long arr[], long long tree[], int node, int start, int end, int L, int R)
{
	if (R<start||L>end)
	{
		return 0;
	}
	if (L <= start && R >= end)
	{
		return tree[node];
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		long long sum_left = qureytree(arr, tree, left_node, start, mid, L, R);
		long long sum_right= qureytree(arr, tree, right_node, mid+1, end, L, R);
		return sum_left + sum_right;
	}
}
void up_data(long long arr[], long long tree[], int node, int start, int end, int idx, int val)
{
	if (start==end)
	{
		arr[start] = arr[start] + val;
		tree[node] = tree[node] + val;
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		if (idx >= start && idx <= mid)
		{
			up_data(arr, tree, left_node, start, mid, idx, val);
		}
		else
		{
			up_data(arr, tree, right_node, mid + 1, end, idx, val);
		}
		tree[node] = tree[left_node] + tree[right_node];
	}
}
int main()
{
	long long arr[1000001], tree[1000001];
	int n, m,i;
	int x, y, k, xx;
	//printf("8888");
	scanf("%d%d", &n, &m);
	for (i = 1; i <= n; i++)
	{
		scanf("%lld", &arr[i]);
	}
	bulid_tree(arr, tree, 1, 1, n);
	//printf("%d")
	while (m--)
	{
		scanf("%d", &xx);
		if (xx == 1)
		{
			scanf("%d%d%d", &x, &y, &k);//将区间 [x, y]内每个数加上 k。
			for (i = x; i <= y; i++)
			{
				up_data(arr, tree, 1, 1, n, i, k);
			}
		}
		if (xx == 2)
		{
			//printf("*****");
			scanf("%d%d", &x, &y);//输出区间 [x, y]内每个数的和。
			printf("%lld\n", qureytree(arr, tree, 1, 1, n, x, y));
		}
	}
}

# 【模板】线段树 2

## 题目描述

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

- 将某区间每一个数乘上 $x$

- 将某区间每一个数加上 $x$

- 求出某区间每一个数的和

## 输入格式

第一行包含三个整数 $n,m,p$,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 $n$ 个用空格分隔的整数,其中第 $i$ 个数字表示数列第 $i$ 项的初始值。

接下来 $m$ 行每行包含若干个整数,表示一个操作,具体如下:

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

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

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

## 输出格式

输出包含若干行整数,即为所有操作 $3$ 的结果。

## 样例 #1

### 样例输入 #1

```
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4
```

### 样例输出 #1

```
17
2
```

## 提示

【数据范围】

对于 $30\%$ 的数据:$n \le 8$,$m \le 10$   
对于 $70\%$ 的数据:$n \le 10^3 $,$ m \le 10^4$   
对于 $100\%$ 的数据:$ n \le 10^5$,$ m \le 10^5$

除样例外,$p = 571373$

(数据已经过加强^\_^)


样例说明:

 ![](https://cdn.luogu.com.cn/upload/pic/2255.png) 

故输出应为 $17$、$2$( $40 \bmod 38 = 2$ )

#include <stdio.h>
void bulid_tree(long long arr[], long long tree[], int node, int start, int end)
{
	if (start == end)
	{
		tree[node] = arr[start];
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		bulid_tree(arr, tree, left_node, start, mid);
		bulid_tree(arr, tree, right_node, mid + 1, end);
		tree[node] = tree[left_node] + tree[right_node];
	}
}
long long qureytree(long long arr[], long long tree[], int node, int start, int end, int L, int R)
{
	if (R<start || L>end)
	{
		return 0;
	}
	if (L <= start && R >= end)
	{
		return tree[node];
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		long long sum_left = qureytree(arr, tree, left_node, start, mid, L, R);
		long long sum_right = qureytree(arr, tree, right_node, mid + 1, end, L, R);
		return sum_left + sum_right;
	}
}
void up_data(long long arr[], long long tree[], int node, int start, int end, int idx, int val)
{
	if (start == end)
	{
		arr[start] = arr[start] + val;
		tree[node] = tree[node] + val;
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		if (idx >= start && idx <= mid)
		{
			up_data(arr, tree, left_node, start, mid, idx, val);
		}
		else
		{
			up_data(arr, tree, right_node, mid + 1, end, idx, val);
		}
		tree[node] = tree[left_node] + tree[right_node];
	}
}
void up_data1(long long arr[], long long tree[], int node, int start, int end, int idx, int val)
{
	if (start == end)
	{
		arr[start] = arr[start]*val;
		tree[node] = tree[node]*val;
		//printf("%d\n", tree[node]);
	}
	else
	{
		int mid = (start + end) / 2;
		int left_node = node * 2;
		int right_node = node * 2 + 1;
		if (idx >= start && idx <= mid)
		{
			up_data1(arr, tree, left_node, start, mid, idx, val);
		}
		else
		{
			up_data1(arr, tree, right_node, mid + 1, end, idx, val);
		}
		tree[node] = tree[left_node] + tree[right_node];
	}
}
int main()
{
	long long arr[1000000], tree[1000000],k;
	int n, m, i,p;
	int x, y, xx;
	//printf("8888");
	scanf("%d%d%d", &n, &m,&p);
	for (i = 1; i <= n; i++)
	{
		scanf("%lld", &arr[i]);
	}
	bulid_tree(arr, tree, 1, 1, n);
	//printf("%d")
	while (m--)
	{
		scanf("%d", &xx);
		if (xx == 1)
		{
			scanf("%d%d%lld", &x, &y, &k);
			for (i = x; i <= y; i++)
			{
				up_data1(arr, tree, 1, 1, n, i, k);
			}
		}
		if (xx == 2)
		{
			scanf("%d%d%lld", &x, &y, &k);//将区间 [x, y]内每个数加上 k。
			for (i = x; i <= y; i++)
			{
				up_data(arr, tree, 1, 1, n, i, k);
			}

		}
		if (xx == 3)
		{
			//printf("*****");
			scanf("%d%d", &x, &y);//输出区间 [x, y]内每个数的和。
			printf("%lld\n", qureytree(arr, tree, 1, 1, n, x, y)%p);
		}
	}
}

等我明天把懒标记的代码搞出来!!!拿下这两个题目!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值