对于线段树,我们一般需要n*4的空间去存储线段树,然后有一种玄学操作是用指针来实现线段树。
#include <inttypes.h> #include <algorithm> #include <cstdio> #include <iostream> #include <vector> #define debug(x) std::cout<< #x << " = " << std::endl; typedef long long int int_t; using std::cin; using std::cout; using std::endl; struct Node{ Node *left,*right; int_t value; int begin,end; int mark; Node(int begin, int end) { this->begin = begin; this->end = end; left = right = NULL; mark = 0; } void add(int_t val) { this->value += (end - begin + 1) * val; mark += val; } void maintain() { if(begin != end) { this->value = left -> value + right->value; } } void pushDown(){ if (mark) { if(begin!= end) { left -> add(mark); right -> add(mark); } mark = 0; } } int_t query (int begin,int end){ if(end < this->begin || begin > this->end) return 0; if(this->begin >= begin && this->end <= end) return this->value; this->pushDown(); return left->query(begin,end) + right->query(begin, end) ; } void add(int begin,int end,int_t val){ if(end < this->begin || begin > this->end) return; if(this->begin >= begin && this->end <= end) { this->add(val); return ; } this->pushDown(); left->add(begin,end,val); right->add(begin,end,val); this->maintain(); } }; char buf[400010*sizeof(Node)]; int used = 0; void* allocate() { return (++used) * sizeof(Node) + buf;} const int MaxN = 200010; Node* tree; int_t a[MaxN]; int n,m; Node* build(int begin,int end){ int mid = (begin + end)/2; Node* node = new(allocate()) Node(begin,end); if(begin != end){ node->left = build(begin ,mid); node->right = build(mid+1,end); } else if(begin == end){ node->value = a[begin]; } node->maintain(); return node; } int main(){ scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++){ scanf("%lld",&a[i]); } tree = build(1,n); for(int i = 1; i <= m; i++){ int opt,x,y; int_t k; scanf("%d",&opt); if(opt == 1){ scanf("%d%d%lld",&x,&y,&k); tree->add(x,y,k); } else { scanf("%d%d",&x,&y); printf("%lld ",tree->query(x,y)); } } return 0; }
接着这个代码说一下指针:
声明指针的方法是 type *x;
例如 int* a;
这样就是声明了一个int类型的指针,没有指向任何的内存。
然后,可以使用 int* a = NULL; 来初始化它,这时候它是一个空指针,一旦对它试图对它操作就会RE,比如 int* a = NULL;
指针本质上一个存贮8比特地址的整形变量,所以应该使用取地址符&来给指针赋值:
int c;
int *a = &c;
我们要通过指针给原本元素赋值:
int c = 0;
int *a = &c;
*a = 5;
这时输出c的值为5
另一种写法是引用,相当于给了某个变量另一个名字,本质仍然是存储地址。
int c = 0;
int &a = c;
a = 5;
与上面的写法是一样的效果。
指针不一定要指向某个变量,可以直接指向某个内存空间。
例如,我声明了一个结构体 struct custom{int a,b;};
然后,我们就可以使用指针指向内存中一个custom类型的内存,例如 custom* cs = new custom;
通过new关键字为这个指针创建一块内存,这个指针就指向它。
但是,如果想访问这个结构体的成员,则不同于原来的 "."访问,需要用 "->"来访问成员。
例如, cs->a = 0; 这样就算是给这个指针指向的结构体内存的a成员赋值为0。
但是,new是系统自动为其分配内存,比较慢,所以我们就可以创建一块内存池: char buf[400010*sizeof(custom)];
然后,用一个变量记录已经使用了内存池中的多少内存:int used = 0;
定义向这个内存池分配内存的函数:void* allocate() { return (++used) * sizeof(custom) + buf;}
used每次加1,记录已经使用了used个大小为sizeof(custom)的内存,然后加号后面是创建的内存池。
于是就可以使用这个内存池来分配内存:
custom *cs;
cs = new(allocate()) custom;
类型后面可以跟上构造函数,假设我们的custom有这样的构造函数:
custom(int a,int b){this->a = a;this->b = b;}
于是分配内存时就可以这样写:cs = new(allocate()) custom(1,2);