【题目】
给定长度为N的数列A,然后输入M行操作指令。
第一类指令形如“C l r d”,表示把数列中第l~r个数都加d。
第二类指令形如“Q X”,表示询问数列中第x个数的值。
对于每个询问,输出一个整数表示答案。
【题解】
区间更新,单点查询,第一反应要么树状数组,要么线段树。个人更擅长线段树,就说说线段树吧。线段树是一种数据结构,是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。设树的最高层即是区间最大的,那么最高层就会分出两个子结点,也就是最高层的左儿子和右儿子,左儿子被分配到
的区间,而右儿子被分配到
的区间,即是区间对半开,如果对上面的
和
不理解的,可以在草稿纸上算一下。这样的话,我们就可以规定每个树上的点所代表的左边界为
、右边界为
,那么当前点所代表的区间就是
,左儿子就是
,右儿子就是
。那么我们就可以利用这个性质来建立线段树,即依照这个性质,建立叶结点到
,也就是单个元素的时候。建立完成后,我们就可以使用线段树节省时间的一个核心思想,
Lazy思想
,也就是线段树中的懒标记
,这个思想就在于每次的更新不会更新到最底层,而是更新到需要更新区间能完全覆盖的区间,比如假设我要更新区间,那么我可能就只更新
和
区间所在的两点,而不是更新深度很深的单点
。那么我们有些小伙伴就会问,这样的话,下次要查询这些单点的话,值还是会跟原来的一样呀。这个的时候就需要
懒标记
来解决。即如果更新到被覆盖到的区间,就不继续往下更新,而是更新好这个区间的值后,做一个标记,比如更新的内容是+2,那么这个标记就+2。下次再查询或者更新到这个区间所在的点的时候,就往下面的左儿子和右儿子传递标记,让左儿子和右儿子所代表的区间更新标记所代表的值,并且左儿子和右儿子也做个这个标记,以之后遍历到它们时,能传给它们的左儿子和右儿子。具体的写法,可以看看下面的这道题的AC代码,试着理解一下。
时间复杂度:线段树的建树复杂度,查询复杂度
,更新复杂度
,此题整体复杂度
#include<iostream> #define Sca(x) scanf("%d",&x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Scl(x) scanf("%lld",&x) #define For(i,x,y) for(int i=x;i<=y;i++) #define Pri(x) printf("%d\n",x) #define Prl(x) printf("%lld\n",x) typedef long long ll; using namespace std; const int N = 1e5+5; struct Segt{ #define lc (p<<1) #define rc (p<<1|1) #define MID (tree[p].l+tree[p].r)>>1 struct Seg{ int l, r; ll sum, lz; void init(int left, int right, int val=0){ l = left; r = right; sum = val; lz = 0; } void update(ll val){ sum += val*(r-l+1); lz += val; } }tree[N<<2]; void pushdown(int p){ tree[p].sum = tree[lc].sum + tree[rc].sum; } void pushup(int p){ tree[lc].update(tree[p].lz); tree[rc].update(tree[p].lz); tree[p].lz = 0; } void build(int l, int r, int p){ tree[p].init(l, r); if(l==r){ Scl(tree[p].sum); return ; } int mid = MID; build(l, mid, lc); build(mid+1, r, rc); pushdown(p); } void update(int l, int r, int p, int val){ if(tree[p].l>=l&&tree[p].r<=r){ tree[p].update(val); return ; } if(tree[p].lz) pushup(p); int mid = MID; if(l<=mid) update(l, r, lc, val); if(r>mid) update(l, r, rc, val); pushdown(p); } ll query(int pos,int p){ ll sum = 0; if(tree[p].l==pos && tree[p].r==pos) return tree[p].sum; if(tree[p].lz) pushup(p); int mid = MID; if(pos<=mid) sum = query(pos, lc); if(pos>mid) sum = query(pos, rc); return sum; } }t; int main(){ int n, m; Sca2(n, m); t.build(1, n, 1); For(i,1,m){ char cmd; scanf(" %c", &cmd); if(cmd == 'C'){ int l, r, d; scanf("%d%d%d", &l, &r, &d); t.update(l, r, 1, d); } else if(cmd == 'Q'){ int pos; Sca(pos); Prl(t.query(pos,1)); } } }