1. 程式人生 > 其它 >數列分塊解決區間更新+區間最值問題

數列分塊解決區間更新+區間最值問題

題目大意:

給定一個大小為 \(n\) 的數列 \(a_1, a_2, \ldots, a_n\),你需要對這個數列進行 \(m\) 次操作,操作包含如下兩種型別:

  • 1 x y z :將區間 \([x,y]\) 範圍內的所有元素更新為 \(z\)(即:\(a_x, a_{x+1}, \ldots, a_y\) 的值都將變為 \(z\));
  • 2 x y :查詢區間 \([x,y]\) 範圍內所有元素的最大值。

對於 \(100\%\) 的資料,\(1 \le n,m \le 2 \times 10^5, op \in [1,2],1 \le x \le y \le n, 1 \le z,a_i \le 10^9\)

解題思路:

  • tag[i] 維護分塊 \(i\) 在整體更新的情況下的數值;
  • sum[i] 維護分塊 \(i\) 在任意時刻的最大值。

示例程式:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200020;
int n, blo, m, a[maxn], bl[maxn], tag[505], mx[505], op, x, y, z;	
// blo=sqrt(n)表示每一個分塊的大小  block分塊 
// bl[i] 表示第i個點所屬的分塊編號 
// 區間更新[l,r]全部更新為z
// tag[x]表示第x個分塊在全部一樣(整體更新)的情況下的數值
// mx[x]表示第x個分塊的最大值 

// 將tag[x]的值全部更新到屬於第x個分塊的所有元素 
void recover(int x) {
	// [(x-1)*blo+1, min(x*blo, n)]
	if (tag[x]) {
		for (int i = (x-1)*blo+1; i <= min(x*blo, n); i ++)
			a[i] = tag[x];
		mx[x] = tag[x]; // 何時何地mx[x]維護的都是區間的最大值 
		tag[x] = 0;
	}
}

// (重新)計算mx[x]的值 
void calmx(int x) {
	mx[x] = 0;
	for (int i = (x-1)*blo+1; i <= min(x*blo, n); i ++)
		mx[x] = max(mx[x], a[i]);
}

void update(int l, int r, int z)
{
	// 1. 處理a[l]所在的分塊(單獨處理) 
	recover(bl[l]);
	for (int i = l; i <= min(bl[l]*blo, r); i ++)
		a[i] = z;
	calmx(bl[l]);
	// 2. 處理a[r]所在的分塊(單獨處理) 
	if (bl[l] != bl[r]) {
		recover(bl[r]);
		for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
			a[i] = z;
		calmx(bl[r]);
	}
	// 3. 處理 bl[l]+1 到 bl[r]-1 這些中間的完整的分塊
	for (int i = bl[l]+1; i < bl[r]; i ++)
		mx[i] = tag[i] = z;
} 

// 查詢區間[l,r]的最大值 
int query(int l, int r)
{
	int res = 0;
	// 1. 處理a[l]所在的分塊(單獨處理) 
	recover(bl[l]);
	for (int i = l; i <= min(bl[l]*blo, r); i ++)
		res = max(res, a[i]);
	// 2. 處理a[r]所在的分塊(單獨處理) 
	if (bl[l] != bl[r]) {
		recover(bl[r]);
		for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
			res = max(res, a[i]);
	}
	// 3. 處理 bl[l]+1 到 bl[r]-1 這些中間的完整的分塊
	for (int i = bl[l]+1; i < bl[r]; i ++)
		res = max(res, mx[i]);
	return res;
} 

int main()
{
	scanf("%d%d", &n, &m);
	blo = sqrt(n);
	for (int i = 1; i <= n; i ++)
	{
		scanf("%d", a+i);
		bl[i] = (i - 1) / blo + 1;
		mx[bl[i]] = max(mx[bl[i]], a[i]);
	}
	while (m --) {
		scanf("%d%d%d", &op, &x, &y);
		if (op == 1) {	// 區間[x,y]全部更新為z 
			scanf("%d", &z);
			update(x, y, z);
		}
		else {	// 查詢區間[x,y]的最大值 
			printf("%d\n", query(x, y));
		}
	}
	return 0;
}