C++ 稀疏表

C++ 稀疏表的作用是解决静态区间的最值问题,每次询问的时间复杂度可以降到O(1)。

稀疏表框架代码,见下

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

// 静态区间最值问题

template<typename T, typename Compare>
class SparseTable {
private:
	vector<T> m_org;
	vector<vector<int>> m_st;
	Compare m_cmp;
	void init();
public:
	SparseTable(const vector<T>& arr);
	int Query(int l, int r);

};

template<typename T, typename Compare>
void SparseTable<T, Compare>::init()
{
	int n = (int)m_org.size();
	int log2n = (int)log2(n) + 1;
	m_st.assign(log2n, vector<int>(n));
	//st[j][i]表示的是[i, i+2^j-1]这个区间中的最值(所在的下标)
    //st[0][i]表示的是[i, i]这个区间中的最值(所在的下标),那就是i
	for (int i = 0; i < n; ++i) {
		m_st[0][i] = i;
	}

	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 0; i + (1 << j) - 1 < n; ++i) {
			// [i, i+(1 << j)-1]
			/*
			  1, st[i][j]的区间长度是2^j
			  2, 把它拆成两个长度为2^(j-1)的区间
			     2.1 一个区间是[i, i+2^(j-1)-1] => st[j-1][i]
				 2.2 一个区间是[i + 2^(j-1), i+2^j-1] => st[j-1][i+1<<(j-1)]
			*/
			int idx1 = m_st[j - 1][i];
			int idx2 = m_st[j - 1][i + (1 << (j - 1))];
			m_st[j][i] = m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

		}
	}

}

template<typename T, typename Compare>
SparseTable<T, Compare>::SparseTable(const vector<T>& arr)
{
	int n = (int)arr.size();
	m_org.assign(arr.begin(), arr.end());
	init();
}
/*
1、对于[l, r]这个区间,可以拆分成两个长度分别为2^k的区间;
   一个区间是以1作为起始的:[1, x];
   一个区间是以r作为结尾的:[y, r];
   并且,这两个区间的并集是[l, r],所以需要满足 x+1>= y
2、以1作为起始的区间长度为2的k次
       它表示的区间就是[1, 1+2^j - 1] => x = 1 + 2^k - 1
   以r结尾的区间长度为2的k次
       它表示的区间就是[r - 2^k + 1, r] => y = r - 2^k + 1
3、1 + 2^k - 1 + 1 >= r - 2^k + 1
   移项: 2^(k+1) >= r - l + 1
   取对数:k >= log2(r - l + 1) - 1
   所以k的值,为log2(r-1+1)取上整 在减一

*/
template<typename T, typename Compare>
int SparseTable<T, Compare>::Query(int l, int r)
{
	if (l == r) {
		return l;
	}
	int k = (int)ceil(log2(r - 1 + 1)) - 1;
	int idx1 = m_st[k][l];
	int idx2 = m_st[k][r - (1 << k) + 1];
	return m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

}

//快速读入一个整数
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}


int main() {
	int n, m;
	n = read();
	m = read();
	vector<int> arr(n);
	for (int i = 0; i < n; ++i) {
		arr[i] = read();
	}
	SparseTable<int, std::greater<int>> st(arr);
	while (m--) {
		int l = read();
		int r = read();
		--l, --r;
		printf("%d\n",arr[st.Query(l, r)] );

	}
	return 0;
}

代码练习1 对应蓝桥云课 区间最大值 代码见下

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

// 静态区间最值问题

template<typename T, typename Compare>
class SparseTable {
private:
	vector<T> m_org;
	vector<vector<int>> m_st;
	Compare m_cmp;
	void init();
public:
	SparseTable(const vector<T>& arr);
	int Query(int l, int r);

};

template<typename T, typename Compare>
void SparseTable<T, Compare>::init()
{
	int n = (int)m_org.size();
	int log2n = (int)log2(n) + 1;
	m_st.assign(log2n, vector<int>(n));
	//st[j][i]表示的是[i, i+2^j-1]这个区间中的最值(所在的下标)
    //st[0][i]表示的是[i, i]这个区间中的最值(所在的下标),那就是i
	for (int i = 0; i < n; ++i) {
		m_st[0][i] = i;
	}

	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 0; i + (1 << j) - 1 < n; ++i) {
			// [i, i+(1 << j)-1]
			/*
			  1, st[i][j]的区间长度是2^j
			  2, 把它拆成两个长度为2^(j-1)的区间
			     2.1 一个区间是[i, i+2^(j-1)-1] => st[j-1][i]
				 2.2 一个区间是[i + 2^(j-1), i+2^j-1] => st[j-1][i+1<<(j-1)]
			*/
			int idx1 = m_st[j - 1][i];
			int idx2 = m_st[j - 1][i + (1 << (j - 1))];
			m_st[j][i] = m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

		}
	}

}

template<typename T, typename Compare>
SparseTable<T, Compare>::SparseTable(const vector<T>& arr)
{
	int n = (int)arr.size();
	m_org.assign(arr.begin(), arr.end());
	init();
}
/*
1、对于[l, r]这个区间,可以拆分成两个长度分别为2^k的区间;
   一个区间是以1作为起始的:[1, x];
   一个区间是以r作为结尾的:[y, r];
   并且,这两个区间的并集是[l, r],所以需要满足 x+1>= y
2、以1作为起始的区间长度为2的k次
       它表示的区间就是[1, 1+2^j - 1] => x = 1 + 2^k - 1
   以r结尾的区间长度为2的k次
       它表示的区间就是[r - 2^k + 1, r] => y = r - 2^k + 1
3、1 + 2^k - 1 + 1 >= r - 2^k + 1
   移项: 2^(k+1) >= r - l + 1
   取对数:k >= log2(r - l + 1) - 1
   所以k的值,为log2(r-1+1)取上整 在减一

*/
template<typename T, typename Compare>
int SparseTable<T, Compare>::Query(int l, int r)
{
	if (l == r) {
		return l;
	}
	int k = (int)ceil(log2(r - l + 1)) - 1;
	int idx1 = m_st[k][l];
	int idx2 = m_st[k][r - (1 << k) + 1];
	return m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

}

//快速读入一个整数
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}


int main() {
	int n, m;
	n = read();
	m = read();
	vector<int> arr(n);
	for (int i = 0; i < n; ++i) {
		arr[i] = read();
	}
	SparseTable<int, std::greater<int>> st(arr);
	while (m--) {
		int l = read();
		int r = read();
		--l, --r;
		printf("%d\n",arr[st.Query(l, r)] );

	}
	return 0;
}

代码练习 2 对应蓝桥云课 附近最小 代码见下

cpp 复制代码
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

// 静态区间最值问题

template<typename T, typename Compare>
class SparseTable {
private:
	vector<T> m_org;
	vector<vector<int>> m_st;
	Compare m_cmp;
	void init();
public:
	SparseTable(const vector<T>& arr);
	int Query(int l, int r);

};

template<typename T, typename Compare>
void SparseTable<T, Compare>::init()
{
	int n = (int)m_org.size();
	int log2n = (int)log2(n) + 1;
	m_st.assign(log2n, vector<int>(n));
	//st[j][i]表示的是[i, i+2^j-1]这个区间中的最值(所在的下标)
    //st[0][i]表示的是[i, i]这个区间中的最值(所在的下标),那就是i
	for (int i = 0; i < n; ++i) {
		m_st[0][i] = i;
	}

	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 0; i + (1 << j) - 1 < n; ++i) {
			// [i, i+(1 << j)-1]
			/*
			  1, st[i][j]的区间长度是2^j
			  2, 把它拆成两个长度为2^(j-1)的区间
			     2.1 一个区间是[i, i+2^(j-1)-1] => st[j-1][i]
				 2.2 一个区间是[i + 2^(j-1), i+2^j-1] => st[j-1][i+1<<(j-1)]
			*/
			int idx1 = m_st[j - 1][i];
			int idx2 = m_st[j - 1][i + (1 << (j - 1))];
			m_st[j][i] = m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

		}
	}

}

template<typename T, typename Compare>
SparseTable<T, Compare>::SparseTable(const vector<T>& arr)
{
	int n = (int)arr.size();
	m_org.assign(arr.begin(), arr.end());
	init();
}
/*
1、对于[l, r]这个区间,可以拆分成两个长度分别为2^k的区间;
   一个区间是以1作为起始的:[1, x];
   一个区间是以r作为结尾的:[y, r];
   并且,这两个区间的并集是[l, r],所以需要满足 x+1>= y
2、以1作为起始的区间长度为2的k次
       它表示的区间就是[1, 1+2^j - 1] => x = 1 + 2^k - 1
   以r结尾的区间长度为2的k次
       它表示的区间就是[r - 2^k + 1, r] => y = r - 2^k + 1
3、1 + 2^k - 1 + 1 >= r - 2^k + 1
   移项: 2^(k+1) >= r - l + 1
   取对数:k >= log2(r - l + 1) - 1
   所以k的值,为log2(r-1+1)取上整 在减一

*/
template<typename T, typename Compare>
int SparseTable<T, Compare>::Query(int l, int r)
{
	if (l == r) {
		return l;
	}
	int k = (int)ceil(log2(r - l + 1)) - 1;
	int idx1 = m_st[k][l];
	int idx2 = m_st[k][r - (1 << k) + 1];
	return m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

}

//快速读入一个整数
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}


int main() {
	int n = read();
	vector<int> arr(n);
	for (int i = 0; i < n; ++i) {
		arr[i] = read();
	}
	int k = read();
	SparseTable<int, std::less<int>> st(arr);
	for (int i = 0; i < n; ++i) {
		int l = i - k;
		int r = i + k;
		if (l < 0) l = 0;
		if (r >= n) r = n - 1;
		printf("%d ", arr[st.Query(l, r)]);
	}
	printf("\n");

	return 0;
}

代码练习 3 对应蓝桥云课 区间选数k 代码见下

cpp 复制代码
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

// 静态区间最值问题

template<typename T, typename Compare>
class SparseTable {
private:
	vector<T> m_org;
	vector<vector<int>> m_st;
	Compare m_cmp;
	void init();
public:
	SparseTable(const vector<T>& arr);
	int Query(int l, int r);

};

template<typename T, typename Compare>
void SparseTable<T, Compare>::init()
{
	int n = (int)m_org.size();
	int log2n = (int)log2(n) + 1;
	m_st.assign(log2n, vector<int>(n));
	//st[j][i]表示的是[i, i+2^j-1]这个区间中的最值(所在的下标)
    //st[0][i]表示的是[i, i]这个区间中的最值(所在的下标),那就是i
	for (int i = 0; i < n; ++i) {
		m_st[0][i] = i;
	}

	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 0; i + (1 << j) - 1 < n; ++i) {
			// [i, i+(1 << j)-1]
			/*
			  1, st[i][j]的区间长度是2^j
			  2, 把它拆成两个长度为2^(j-1)的区间
			     2.1 一个区间是[i, i+2^(j-1)-1] => st[j-1][i]
				 2.2 一个区间是[i + 2^(j-1), i+2^j-1] => st[j-1][i+1<<(j-1)]
			*/
			int idx1 = m_st[j - 1][i];
			int idx2 = m_st[j - 1][i + (1 << (j - 1))];
			m_st[j][i] = m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

		}
	}

}

template<typename T, typename Compare>
SparseTable<T, Compare>::SparseTable(const vector<T>& arr)
{
	int n = (int)arr.size();
	m_org.assign(arr.begin(), arr.end());
	init();
}
/*
1、对于[l, r]这个区间,可以拆分成两个长度分别为2^k的区间;
   一个区间是以1作为起始的:[1, x];
   一个区间是以r作为结尾的:[y, r];
   并且,这两个区间的并集是[l, r],所以需要满足 x+1>= y
2、以1作为起始的区间长度为2的k次
       它表示的区间就是[1, 1+2^j - 1] => x = 1 + 2^k - 1
   以r结尾的区间长度为2的k次
       它表示的区间就是[r - 2^k + 1, r] => y = r - 2^k + 1
3、1 + 2^k - 1 + 1 >= r - 2^k + 1
   移项: 2^(k+1) >= r - l + 1
   取对数:k >= log2(r - l + 1) - 1
   所以k的值,为log2(r-1+1)取上整 在减一

*/
template<typename T, typename Compare>
int SparseTable<T, Compare>::Query(int l, int r)
{
	if (l == r) {
		return l;
	}
	int k = (int)ceil(log2(r - l + 1)) - 1;
	int idx1 = m_st[k][l];
	int idx2 = m_st[k][r - (1 << k) + 1];
	return m_cmp(m_org[idx1], m_org[idx2]) ? idx1 : idx2;

}

//快速读入一个整数
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}


int main() {
	int n, m;
	n = read();
	m = read();
	vector<int> arr(n);
	for (int i = 0; i < n; ++i) {
		arr[i] = read();
	}
	SparseTable<int, std::less<int>> stl(arr);
	SparseTable<int, std::greater<int>> stg(arr);
	long long minsum = 0, maxsum = 0;
	while (m--) {
		int l = read();
		int r = read();
		--l, --r;
		minsum += arr[stl.Query(l, r)];
		maxsum += arr[stg.Query(l, r)];

	}
	cout << minsum << ' ' << maxsum << endl;
	return 0;
}
相关推荐
2601_962071576 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
淡海水7 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
leobertlan7 小时前
好玩系列:训练一个神经网络模型指导小孩玩游戏2-大局观教练
算法
CS_Zero8 小时前
C语言sizeof是函数吗?
c语言·开发语言
Tisfy8 小时前
LeetCode 3876.构造奇偶一致的数组 II:三种情况分类讨论(其实还是脑筋急转弯)
算法·leetcode·题解·脑筋急转弯
-今昭-9 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
乐迪信息9 小时前
智慧港口船舶AI算法实现在线状态监测
大数据·人工智能·深度学习·算法·计算机视觉
黑马程序员毕设11 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
木井巳11 小时前
【BFS/DFS 解决 FloodFill 算法】太平洋大西洋水流问题
java·算法·leetcode·深度优先·广度优先·宽度优先·推荐算法
心抵鹊11 小时前
归并排序之翻转对(hard)
数据结构·算法