-
条件:无
-
题目:
洛谷树状数组模板题1
洛谷树状数组模板题2 -
原理:无
-
代码:
附上某题解的好图,见题解区
第一题/** * mootable */ #include <iostream> #include <iomanip> #include <algorithm> //sort #include <map> #include <queue> #include <deque> //双端队列,头可插,尾可插 #include <string> #include <cstring> #include <stack> #include <cmath> #include <fstream> #include <ctime> #include <climits> //数值的限制范围 //(double)clock() / CLOCKS_PER_SEC <= 0.95 限制0.95s跑完 using namespace std; class Solver { //通用属性 public: Solver() { //取消和c输出输入的同步,加快读取 ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); } //通用方法 public: void SaveCpp(string name) const { fstream input; fstream output; input.open("moota.cpp", ios::in); const string file = name + ".cpp"; output.open(file.c_str(), ios::out); char c = 'O'; while (!input.eof()) { input.get(c); output << c; } input.close(); output.close(); } protected: //待实现方法 virtual void BeginPlay() { }; virtual void Playing() { }; virtual void EndPlay() { }; public: //外观模式 void Play() { BeginPlay(); Playing(); EndPlay(); } }; class SpecialSolver : public Solver { public: typedef long long lld; static const lld MAX = static_cast<lld>(1e6); static const lld INF = static_cast<lld>(1e18); private: //实例属性 lld n, m; lld a[MAX]; lld tree[MAX]; private: //实例方法 //返回x管辖的数的个数 //计算原理不用了解 lld Lowbit(int x) { return x & (-x); } void SingleAdd(const lld id, const lld value) { //对每个管辖id的数进行Add操作 //因为存在(管辖id的数)管辖的数=2*(id)管辖的数,类似倍增。 //所以id+Lowbit(id) 得到 下一个管辖id的数。 //不信自己画个图看看。 for (int i = id; i <= n; i += Lowbit(i)) { tree[i] += value; } } lld SingleSum(const lld id) { lld sum = 0; //Lowbit(id)是id管辖的数。 //id-Lowbit(id) 则会跳出id管辖的范围,得到 前一个管辖的数。 //等于把求1-id的和进行了划分操作。 //不信自己画个图看看。 for (int i = id; i >=1 ; i -= Lowbit(i)) { sum += tree[i]; } return sum; } protected: virtual void BeginPlay() override { Solver::BeginPlay(); cin >> n >> m; for (lld i = 1; i <= n; ++i) { cin >> a[i]; SingleAdd(i,a[i]); } }; virtual void Playing() override { Solver::Playing(); lld order, x, y; while (m--) { cin >> order >> x >> y; switch (order) { case 1: SingleAdd(x, y); break; case 2: cout << SingleSum(y) - SingleSum(x - 1) << "\n"; break; default: cout << "未处理情况: " << order; break; } } }; virtual void EndPlay() override { Solver::EndPlay(); }; }; SpecialSolver specialSolver; int main() { //注意改名字 //specialSolver.SaveCpp("树状数组模板"); specialSolver.Play(); }
第二题
/** * mootable */ #include <iostream> #include <iomanip> #include <algorithm> //sort #include <map> #include <queue> #include <deque> //双端队列,头可插,尾可插 #include <string> #include <cstring> #include <stack> #include <cmath> #include <fstream> #include <ctime> #include <climits> //数值的限制范围 //(double)clock() / CLOCKS_PER_SEC <= 0.95 限制0.95s跑完 using namespace std; class Solver { //通用属性 public: Solver() { //取消和c输出输入的同步,加快读取 ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); } //通用方法 public: void SaveCpp(string name) const { fstream input; fstream output; input.open("moota.cpp", ios::in); const string file = name + ".cpp"; output.open(file.c_str(), ios::out); char c = 'O'; while (!input.eof()) { input.get(c); output << c; } input.close(); output.close(); } protected: //待实现方法 virtual void BeginPlay() { }; virtual void Playing() { }; virtual void EndPlay() { }; public: //外观模式 void Play() { BeginPlay(); Playing(); EndPlay(); } }; //要知道树状数组维护的只是一个区间的值。 //所以这个区间的意义是什么是与维护无关的。 //比如:这个区间的意义是差分。 //那么:求RangeSum(id), //表面求的是:tree[1]-tree[id]得和, //实际效果是:可以求出a[id]的值。 //那么:求RangeAdd(left,value),RangeAdd(right+1,-value), //表面求的是:tree[left]+=value,tree[right+1]+=-value; //实际效果是:使得a[left]-a[right]区间的值都加上value; //所以什么区间,单点需要看对实际数据的影响。 //这道题tree数组的区间求和实际是a数组的单点求和... class SpecialSolver : public Solver { public: typedef long long lld; static const lld MAX = static_cast<lld>(1e6); static const lld INF = static_cast<lld>(1e18); private: //实例属性 lld n, m; lld a[MAX]; lld tree[MAX]; private: //实例方法 //返回x管辖的数的个数 //计算原理不用了解 lld Lowbit(int x) { return x & (-x); } //1题 //单点修改 void SingleAdd(const lld id, const lld value) { //对每个管辖id的数进行Add操作 //因为存在(管辖id的数)管辖的数=2*(id)管辖的数,类似倍增。 //所以id+Lowbit(id) 得到 下一个管辖id的数。 //不信自己画个图看看。 for (int i = id; i <= n; i += Lowbit(i)) { tree[i] += value; } } //区间求和 lld RangeSum(const lld id) { lld sum = 0; //Lowbit(id)是id管辖的数。 //id-Lowbit(id) 则会跳出id管辖的范围,得到 前一个管辖的数。 //等于把求1-id的和进行了划分操作。 //不信自己画个图看看。 for (int i = id; i >=1 ; i -= Lowbit(i)) { sum += tree[i]; } return sum; } //2题 //单点求和 void SingleAdd(const lld id) { RangeSum(id); } //区间修改 void RangeAdd(const lld left,const lld right,const int value) { SingleAdd(left,value); SingleAdd(right+1,-value); } protected: virtual void BeginPlay() override { Solver::BeginPlay(); cin >> n >> m; int pre=0,cur=0; for (lld i = 1; i <= n; ++i) { cin>>cur; SingleAdd(i,cur-pre);//差分 pre=cur; } }; virtual void Playing() override { Solver::Playing(); lld order, x, y,k; while (m--) { cin >> order; switch (order) { case 1: cin>>x>>y>>k; RangeAdd(x, y,k); break; case 2: cin>>x; cout << RangeSum(x)<< "\n"; break; default: cout << "未处理情况: " << order; break; } } }; virtual void EndPlay() override { Solver::EndPlay(); }; }; SpecialSolver specialSolver; int main() { //注意改名字 //specialSolver.SaveCpp("树状数组模板"); specialSolver.Play(); }