【线段树】P1438 无聊的数列|普及+

本文涉及知识点

C++线段树

P1438 无聊的数列

题目背景

无聊的 YYB 总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的 YYB 想出了一道无聊的题:无聊的数列。。。

题目描述

维护一个数列 a i a_i ai,支持两种操作:

  • 1 l r K D:给出一个长度等于 r − l + 1 r-l+1 rl+1 的等差数列,首项为 K K K,公差为 D D D,并将它对应加到 [ l , r ] [l,r] [l,r] 范围中的每一个数上。即:令 a l = a l + K , a l + 1 = a l + 1 + K + D … a r = a r + K + ( r − l ) × D a_l=a_l+K,a_{l+1}=a_{l+1}+K+D\ldots a_r=a_r+K+(r-l) \times D al=al+K,al+1=al+1+K+Dar=ar+K+(rl)×D

  • 2 p:询问序列的第 p p p 个数的值 a p a_p ap

输入格式

第一行两个整数数 n , m n,m n,m 表示数列长度和操作个数。

第二行 n n n 个整数,第 i i i 个数表示 a i a_i ai

接下来的 m m m 行,每行先输入一个整数 o p t opt opt

o p t = 1 opt=1 opt=1 则再输入四个整数 l   r   K   D l\ r\ K\ D l r K D

o p t = 2 opt=2 opt=2 则再输入一个整数 p p p

输出格式

对于每个询问,一行一个整数表示答案。

输入输出样例 #1

输入 #1

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

输出 #1

6

说明/提示

数据规模与约定

对于 100 % 100\% 100% 数据, 0 ≤ n , m ≤ 1 0 5 , − 200 ≤ a i , K , D ≤ 200 , 1 ≤ l ≤ r ≤ n , 1 ≤ p ≤ n 0\le n,m \le 10^5,-200\le a_i,K,D\le 200, 1 \leq l \leq r \leq n, 1 \leq p \leq n 0n,m105,200ai,K,D200,1lrn,1pn

线段树

L,R 增加KD等差数列。    ⟺    \iff [L,R]之间的数,全部加上 k + iD - LD ,即Di+(k-LD)
线段树TSave long long:保存各区间的值。由于只会查询叶子节点,故非此叶子节点任意。
TRecord <long long,long long> 累加:D 和 累加 (K - LD)

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 12 * 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
	}
private:
	char  puffer[N], * m_p;
};

template<int N = 12 * 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {
		fread(buffer, 1, N, stdin);
	}
	inline int Read() {
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		return f ? -x : x;
	}
private:
	char buffer[N], * S = buffer;
};

template<class TSave, class TRecord >
class CRangUpdateLineTree
{
protected:
	virtual void OnQuery(TSave& ans, const TSave& save, const int& iSaveLeft, const int& iSaveRight) = 0;
	virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) = 0;
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight) = 0;
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) = 0;
};

template<class TSave, class TRecord >
class CVectorRangeUpdateLineTree : public CRangUpdateLineTree<TSave, TRecord>
{
public:
	CVectorRangeUpdateLineTree(int iEleSize, TSave tDefault, TRecord tRecordNull) :m_iEleSize(iEleSize), m_tDefault(tDefault)
		, m_save(iEleSize * 4, tDefault), m_record(iEleSize * 4, tRecordNull) {
		m_recordNull = tRecordNull;
	}
	void Update(int iLeftIndex, int iRightIndex, TRecord value)
	{
		Update(1, 0, m_iEleSize - 1, iLeftIndex, iRightIndex, value);
	}
	TSave Query(int leftIndex, int rightIndex) {
		return Query(leftIndex, rightIndex, m_tDefault);
	}
	TSave Query(int leftIndex, int rightIndex, const TSave& tDefault) {
		TSave ans = tDefault;
		Query(ans, 1, 0, m_iEleSize - 1, leftIndex, rightIndex);
		return ans;
	}
	//void Init() {
	//	Init(1, 0, m_iEleSize - 1);
	//}
	TSave QueryAll() {
		return m_save[1];
	}
	void swap(CVectorRangeUpdateLineTree<TSave, TRecord>& other) {
		m_save.swap(other.m_save);
		m_record.swap(other.m_record);
		std::swap(m_recordNull, other.m_recordNull);
		assert(m_iEleSize == other.m_iEleSize);
	}
protected:
	//void Init(int iNodeNO, int iSaveLeft, int iSaveRight)
	//{
	//	if (iSaveLeft == iSaveRight) {
	//		this->OnInit(m_save[iNodeNO], iSaveLeft);
	//		return;
	//	}
	//	const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
	//	Init(iNodeNO * 2, iSaveLeft, mid);
	//	Init(iNodeNO * 2 + 1, mid + 1, iSaveRight);
	//	this->OnUpdateParent(m_save[iNodeNO], m_save[iNodeNO * 2], m_save[iNodeNO * 2 + 1], iSaveLeft, iSaveRight);
	//}
	void Query(TSave& ans, int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) {
		if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {
			this->OnQuery(ans, m_save[iNodeNO], iSaveLeft, iSaveRight);
			return;
		}
		if (iSaveLeft == iSaveRight) {//没有子节点
			return;
		}
		Fresh(iNodeNO, iSaveLeft, iSaveRight);
		const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (mid >= iQueryLeft) {
			Query(ans, iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight);
		}
		if (mid + 1 <= iQueryRight) {
			Query(ans, iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight);
		}
	}
	void Update(int iNode, int iSaveLeft, int iSaveRight, int iOpeLeft, int iOpeRight, TRecord value)
	{
		if ((iOpeLeft <= iSaveLeft) && (iOpeRight >= iSaveRight))
		{
			this->OnUpdate(m_save[iNode], iSaveLeft, iSaveRight, value);
			this->OnUpdateRecord(m_record[iNode], value);
			return;
		}
		if (iSaveLeft == iSaveRight) {
			return;//没有子节点
		}
		Fresh(iNode, iSaveLeft, iSaveRight);
		const int iMid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
		if (iMid >= iOpeLeft)
		{
			Update(iNode * 2, iSaveLeft, iMid, iOpeLeft, iOpeRight, value);
		}
		if (iMid + 1 <= iOpeRight)
		{
			Update(iNode * 2 + 1, iMid + 1, iSaveRight, iOpeLeft, iOpeRight, value);
		}
		// 如果有后代,至少两个后代
		this->OnUpdateParent(m_save[iNode], m_save[iNode * 2], m_save[iNode * 2 + 1], iSaveLeft, iSaveRight);
	}
	void Fresh(int iNode, int iDataLeft, int iDataRight)
	{
		if (m_recordNull == m_record[iNode])
		{
			return;
		}
		const int iMid = iDataLeft + (iDataRight - iDataLeft) / 2;
		Update(iNode * 2, iDataLeft, iMid, iDataLeft, iMid, m_record[iNode]);
		Update(iNode * 2 + 1, iMid + 1, iDataRight, iMid + 1, iDataRight, m_record[iNode]);
		m_record[iNode] = m_recordNull;
	}
	vector<TSave> m_save;
	vector<TRecord> m_record;
	TRecord m_recordNull;
	TSave m_tDefault;
	const int m_iEleSize;
};

typedef long long TSave;
typedef pair<long long, long long>  TRecord;
class  CMyLineTree : public  CVectorRangeUpdateLineTree<TSave, TRecord>
{
public:
	using CVectorRangeUpdateLineTree::CVectorRangeUpdateLineTree;
protected:
	virtual void OnQuery(TSave& ans, const TSave& save, const int& iSaveLeft, const int& iSaveRight) {
		ans = save;
	}
	virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) override
	{
		save += iSaveLeft * update.first + update.second;
	}
	virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight)  override
	{

	}
	virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override
	{
		old.first += newRecord.first;
		old.second += newRecord.second;
	}
};
class Solution {
public:
	vector<long long> Ans(const vector<int>& a, vector<tuple<int, int, int, int>>& que) {
		const int N = a.size();
		CMyLineTree lineTree(N + 1, 0, { 0,0 });
		for (int i = 1; i <= N; i++) {
			lineTree.Update(i, i, { 0,a[i - 1] });
		}
		vector<long long> ans;
		for (const auto& [left, r, K, D] : que) {
			if (0 == r) {
				ans.emplace_back(lineTree.Query(left, left));
			}
			else
			{
				lineTree.Update(left, r, { D,K - left * D });
			}
		}
		return ans;
	}
};

int main() {	
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG		
	int n, m;
	cin >> n >> m;
		auto a = Read<int>(n);
		vector<tuple<int, int, int, int>> que(m);
		int kind;
		for (int i = 0; i < m; i++) {
			cin >> kind >> get<0>(que[i]);
			if (1 == kind) {
				cin >> get<1>(que[i]) >> get<2>(que[i]) >> get<3>(que[i]);
			}
		}
#ifdef _DEBUG		
		/*printf("T=%d,", T);*/
		/*Out(a, "a=");
		Out(que, "que=");*/
#endif // DEBUG	
		auto res = Solution().Ans(a,que);
		for (const auto& i : res)
		{
			cout << i << endl;
		}
	return 0;
}

单元测试

vector<int> a;
		vector<tuple<int, int, int, int>> que;
		TEST_METHOD(TestMethod11)
		{
			a = { 1,2,3,4,5 },que = { {2,4,1,2},{3,0,0,0} };
			auto res = Solution().Ans(a,que);
			AssertEx({ 6LL }, res);
		}	

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值