#include #include #include using namespace std; 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 << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return x * f; } const int N = 1e5 + 10; int n; int t[N << 1]; void build() { for (int i = 0; i < n; i++) t[n + i] = read(); for (int i = n - 1; i; i--) t[i] = t[i << 1] + t[i << 1 | 1]; } void modify(int l, int r, int value) { for (l += n, r += n; l < r; l >>= 1, r >>= 1) { if (l & 1) t[l++] += value; if (r & 1) t[--r] += value; } } //区间查询 int query(int l, int r) { // sum on interval [l, r) int res = 0; for (l += n, r += n; l < r; l >>= 1, r >>= 1) { //① 如果l是奇数,l是父亲的右孩子(看一下上面的图,确实是右孩子) // (1) 此时,只有它自己有用,它的父亲不包含在查询区间内。自己加上,把略过即可 // (2) 怎么略过呢?巧妙的办法:本轮次不加自己父亲,下软移动到右边的兄弟节点的父亲上去! l= (l+1) >> 1 //② 如果l是偶数,那么l就是父亲的左儿子,整个区间完整命中,直接研究它父亲的sum即可, l>>=1 if (l & 1) res += t[l++]; //与上面代码对称的写法 if (r & 1) res += t[--r]; } return res; } int main() { //文件输入输出 #ifndef ONLINE_JUDGE freopen("zkw_Study_2.in", "r", stdin); #endif n = read(); build(); modify(0, 1, 1); //修改[0,1)也是就是修改下标0的值为1 for (int i = n; i < 2 * n; i++) printf("%d ", t[i]); puts(""); printf("%d\n", query(1, 3)); //下标从0开始,查询[1,3)=3 return 0; //感悟:这东西区间修改,区间查询就OK了,没有什么单点修改,单点查询,直接上区间修改、区间查询不就行了吗? }