樹狀數組做用c++
核心思想
把前n個數劃分爲log(n)個區間,分別維護這log(n)個區間的和,在求解前綴和Sn的時候,從求解n個數字的和變成求解log(n)個區間的和來加快運算算法
具體操做
維護log(n)個區間,每一個區間用數組c來維護區間和。單點修改x的時候,修改x所在的c,而後一路向上修改父節點的c;區間查詢[1 ~ x]的時候,從x開始向前,把x的全部兄弟節點的c都加起來數組
c數組性質網絡
#include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = 5e5 + 10; int a[N], n, m; LL c[N], sum[N]; // c[x] = a[x-lowbit(x) + 1] + ... + a[x],sum[x]=a[1]+...+a[x] int lowbit(int x) { return x & (-x); } // 單點修改 void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; // 不斷往父節點跳 } // 查詢Sx前綴和 LL query(int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; // 不斷往前一個兄弟節點跳 return res; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); // 讀入每一個元素 sum[i] = sum[i - 1] + a[i]; // 計算前綴和 } for (int i = 1; i <= n; ++i) c[i] = sum[i] - sum[i - lowbit(i)]; // 初始化c數組 for (int i = 1, op, x, y; i <= m; ++i) { // m個操做 scanf("%d%d%d", &op, &x, &y); if (op == 1) add(x, y); // 單點修改 else printf("%lld\n", query(y) - query(x - 1)); // 區間查詢 } return 0; }
/* 本題要進行的是 區間增長+單點詢問,而樹狀數組能進行的操做爲 單點增長 + 區間查詢 能夠經過差分來實現這個轉變,使得樹狀數組能夠完成區間增長 + 單點詢問 即:維護一個b數組表示當前a數組的增長減小狀況,一旦a數組[l, r]增長d, 那麼b[l] += d, b[r + 1] -= d 所以咱們能夠用樹狀數組維護這個b數組的前綴和,即維護一個c數組,記錄b數組的前綴和,那麼經過add()操做就能改變前綴和 然後每次詢問單點時,咱們查詢b數組的前綴和就能夠知道a點的增長減小狀況 */ #include<bits/stdc++.h> using namespace std; typedef long long LL; int n, m; int const N = 1e5 + 10; int c[N], a[N]; // c[x]維護的是b數組的前綴和 int lowbit(int x) { return x & -x; } // 單點修改 void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; // 不斷往父節點跳 } // 查詢Sx前綴和 LL query(int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; // 不斷往前一個兄弟節點跳 return res; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); // 讀入a[i] while (m--) { string op; int l, r, d, x; cin >> op; // 單點查詢 if (op[0] == 'Q') { scanf("%d", &x); printf("%lld\n", query(x) + a[x]); // x點的變化值+a[x] } else { scanf("%d %d %d", &l, &r, &d); add(l, d), add(r + 1, -d); // 區間增長 } } return 0; }
/* 對於第一種處理方式,能夠利用差分結合acwing242一個簡單的整數問題1的方式來處理; 對於問題而求區間和,那麼考慮維護a的前綴和 a1+a2+...+ax=(b1)+(b1+b2)+(b1+b2+b3)+...+(b1+b2+b3+..+bx) =(1+x)累加從1到x(bi)-累加從1到x(i*bi) 咱們能夠使用兩個樹狀數組分別維護bi和i*bi的前綴和 */ #include<bits/stdc++.h> using namespace std; typedef long long LL; int n, m; int const N = 1e5 + 10; LL c1[N], c2[N]; // c1維護bi的前綴和,c2維護i*bi的前綴和 int a[N]; LL s[N]; int lowbit(int x) { return x & -x; } // 單點修改 void add(LL c[], int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += (LL)y; } // 前綴和 LL query(LL c[], int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); s[i] = s[i - 1] + 0ll + a[i]; // 計算原來的a[i]的前綴和 } while (m--) { string op; int l, r, d; cin >> op; if (op[0] == 'C') { cin >> l >> r >> d; add(c1, l, d), add(c1, r + 1, -d); // 把[l, r]加d對c1來講就是把l和r+1分別增長d和減去d add(c2, l, l * d), add(c2, r + 1, -(r + 1) * d); // 把[l, r]加d對c1來講就是把l和r+1分別增長l*d和減去(r+1)*d } else { cin >> l >> r; LL res = s[r] - s[l - 1]; // 原來的區間和 res += ((1 + r) * query(c1, r) - query(c2, r) - (l * query(c1, l - 1) - query(c2, l - 1))); // 加上改變值 printf("%lld\n", res ); } } return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = (1 << 12) + 10; LL n, m, c[N][N]; // c[x][y] = 累加c[i][j], i∈(i-lowbit(i)+1, x), j∈(j-lowbit(j)+1, y) int lowbit(int x) { return x & -x; } // 單點修改 void add(int x, int y, int z) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= m; j += lowbit(j)) c[i][j] += (LL)z; } // 區間查詢 LL query(int x, int y) { LL res = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) res += c[i][j]; return res; } int main() { cin >> n >> m; int op, x1, y1, k, x2, y2; while (scanf("%d", &op) != EOF) { if (op == 1) { // 單點修改,把(x1, y1)加上k scanf("%d%d%d", &x1, &y1, &k); add(x1, y1, k); } else { // 區間查詢,獲得以左上角爲(x1, y1),右下角爲(x2, y2)的矩形的區間和 scanf("%d%d%d%d", &x1, &y1, &x2, &y2); printf("%lld\n", query(x2, y2) - query(x2, y1 - 1) - query(x1 - 1, y2) + query(x1 - 1, y1 - 1)); } } return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = (1 << 12) + 10; LL c[N][N], n, m, op; // c[x][y]維護b[i][j]的前綴和 int lowbit(int x) { return x & (-x); } // 二維單點修改 void add(int x, int y, int z) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= m; j += lowbit(j)) c[i][j] += z; } // 求二維前綴和 LL query(int x, int y) { LL res = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) res += c[i][j]; return res; } int main() { cin >> n >> m; while (scanf("%lld", &op) != EOF) { int x1, y1, k, x2, y2; if (op == 1) { // 區間增長:轉化爲二維差分數組的增長 scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, & k); add(x2 + 1, y2 + 1, k); add(x1, y1, k); add(x2 + 1, y1, -k); add(x1, y2 + 1, -k); } else { // 單點查詢:轉化爲二維差分數組求前綴和 scanf("%d%d", &x1, &y1); printf("%lld\n", query(x1, y1)); } } return 0; }
/* 累加aij(i∈[1, x], j∈[1, y])=(x+1)*(y+1)累加bij - (x+1)累加j*bij - (y + 1)累加i*bij + 累加i*j*bij c1維護bij的二維前綴和,c2維護j*bij的二維前綴和,c3維護i*bij的二維前綴和,c4維護i*j*bij的二維前綴和 */ #include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = (1 << 12) + 10; LL c1[N][N], c2[N][N], c3[N][N], c4[N][N], n, m, op; // c[x][y]維護b[i][j]的前綴和 int lowbit(int x) { return x & (-x); } // 二維單點修改 void add(LL c[][N], int x, int y, LL z) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= m; j += lowbit(j)) c[i][j] += z; } // 求二維前綴和 LL query(LL c[][N], int x, int y) { LL res = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) res += c[i][j]; return res; } LL Query(int x, int y) { return (x + 1) * (y + 1) * query(c1, x, y) - (x + 1) * query(c2, x, y) - (y + 1) * query(c3, x, y) + query(c4, x, y); } int main() { cin >> n >> m; while (scanf("%lld", &op) != EOF) { int x1, y1, x2, y2, k; if (op == 1) { scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &k); add(c1, x2 + 1, y2 + 1, k), add(c1, x1, y1, k), add(c1, x2 + 1, y1, -k), add(c1, x1, y2 + 1, -k); add(c2, x2 + 1, y2 + 1, k * (y2 + 1)), add(c2, x1, y1, k * y1), add(c2, x2 + 1, y1, -k * y1), add(c2, x1, y2 + 1, -k * (y2 + 1)); add(c3, x2 + 1, y2 + 1, k * (x2 + 1)), add(c3, x1, y1, k * x1), add(c3, x2 + 1, y1, -k * (x2 + 1)), add(c3, x1, y2 + 1, -k * x1); add(c4, x2 + 1, y2 + 1, k * (x2 + 1) * (y2 + 1)), add(c4, x1, y1, k * x1 * y1), add(c4, x2 + 1, y1, -k * (x2 + 1) * y1), add(c4, x1, y2 + 1, -k * x1 * (y2 + 1)); } else { scanf("%d%d%d%d", &x1, &y1, &x2, &y2); printf("%lld\n", Query(x2, y2) + Query(x1 - 1, y1 - 1) - Query(x1 - 1, y2) - Query(x2, y1 - 1)); } } return 0; }
POJ2299 Ultra-QuickSort
有多個測試樣例,每一個測試樣例給定n個數字a[i], 求這n個數字的逆序對數目
n~5e5, a[i]~999999999測試
// 使用樹狀數組去維護每一個數字出現的次數的前綴和,c[i]記錄i這個節點管轄的幾個點的出現次數 // 統計時,只要統計每一個點右邊有多少個比他小便可 // 注意須要離散化 #include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = 5e5 + 10; LL c[N], n, a[N]; vector<LL> v; unordered_map<LL, int> H; int lowbit(int x) { return x & -x; } void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } LL query(int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { while (scanf("%lld", &n) != EOF && n) { memset(c, 0, sizeof c); v.clear(); H.clear(); for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); v.push_back(a[i]); } sort(v.begin(), v.end()); int cnt = 1; for (int i = 0; i < v.size(); ++i) H[v[i]] = cnt++; // 離散化 LL res = 0; for (int i = n; i >= 1; --i) { // 計算每一個H[a[i]出現的次數 res += query(H[a[i]] - 1); // 加上H[a[i]-1出現的總次數 add(H[a[i]], 1); // 次數加一 } printf("%lld\n", res); } return 0; }
acwing241樓蘭圖騰
有n個點,座標分別爲(1, y1), (2, y2), (3, y3), ..., (n, yn)
若是三個點(i, yi), (j, yj), (k, yk) 知足1 ≤ i < j < k ≤ n且yi > yj, yj < yk,則稱這三個點構成V圖騰;
若是三個點(i, yi), (j, yj), (k, yk)知足1 ≤ i < j < k ≤ n 且yi < yj, yj > yk,則稱這三個點構成∧圖騰;
求有多少個V圖騰和∧圖騰
n ~ 2e5, yi ~ 2e5ui
/* 使用樹狀數組去維護每一個數字出現的次數的前綴和,c[i]記錄i這個節點管轄的幾個點的出現次數 統計時,只要統計每一個點左邊有多少個比他大,右邊有多少個比他大,而後相乘就可以知道出現多少個^; 統計V則相反 */ #include<bits/stdc++.h> using namespace std; typedef long long LL; int n; int const N = 2e5 + 10; int a[N], c[N]; int gre[N], low[N]; int lowbit(int x) { return x & -x; } void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { cin >> n; for (int i = 0; i < n; ++i) scanf("%d", &a[i]); LL res1 = 0, res2 = 0; for (int i = 0; i < n; ++i) { int y = a[i]; gre[i] = query(n) - query(y); low[i] = query(y - 1); add(y , 1); } memset(c, 0, sizeof c); for (int i = n - 1; i >= 0; --i) { int y = a[i]; res1 += (LL)gre[i] * (query(n) - query(y)); res2 += (LL)low[i] * query(y - 1); add(y , 1); } cout << res1 << " " << res2 << endl; return 0; }
POJ3067 Japan
有兩個海岸,兩個海岸分別有N個點和M個點,從北到南編號爲1,2,3...,N。如今在兩個海岸之間建設橋樑。每一個橋樑給出x, y,表示第一個海岸的x點到第二個海岸的y點有橋樑。問造成交叉的橋樑數目有多少。
N、M~1e3spa
// 設橋樑1爲(x1, y1),橋樑2爲(x2, y2),當(x1-x2)*(y1-y2)<0時兩個橋樑交叉 // 所以只須要把y按照從大到小的順序排序,每次觀察小於當前x的數有多少個便可 // 這樣就轉化爲求解逆序對的問題,樹狀數組求之 #include<bits/stdc++.h> using namespace std; typedef long long LL; int n, m, T, q, kase = 1; int const N = 1e3 + 10; LL c[N]; struct Line { int x, y; bool operator<(const struct Line &w) const { if (y == w.y) return x > w.x; else return y > w.y; } }line[N * N]; LL lowbit(LL x) { return x & -x; } // 單點修改 void add(int x, LL y) { for (int i = x; i <= N; i += lowbit(i)) c[i] += y; // 不斷往父節點跳 } // 查詢Sx前綴和 LL query(int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; // 不斷往前一個兄弟節點跳 return res; } int main() { cin >> T; while (T--) { cin >> n >> m >> q; memset(c, 0, sizeof c); for (int i = 1; i <= q; ++i) scanf("%d%d", &line[i].x, &line[i].y); sort(line + 1, line + 1 + q); // 按y從大到小排序 LL res = 0; for (int i = 1; i <= q; ++i) { res += query(line[i].x - 1); // 計算小於當前x的數目 add(line[i].x, 1); // 當前x加一 } printf("Test case %d: %lld\n", kase++, res); } return 0; }
POJ 2352 Stars
給一個二維網格,而後給定網格上n個點,計算每一個點的左下方的點個數
點的輸入是y從小到大輸入,若是y相同,那麼x從小到大輸入。
n~15000, x、y~32000code
// 題目順序都處理好了。注意向右平移一個單位,由於0號位置也有star #include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = 2e5 + 10; int n; LL c[N], res[N]; int lowbit(int x) { return x & (-x); } // 單點修改 void add(int x, int y) { for (int i = x; i <= N; i += lowbit(i)) c[i] += y; // 不斷往父節點跳 } // 查詢Sx前綴和 LL query(int x) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; // 不斷往前一個兄弟節點跳 return res; } int main() { cin >> n; for (int i = 1, x, y; i <= n; ++i) { scanf("%d%d", &x, &y); x++; // 樹狀數組不能統計0 res[query(x)]++; // 計數 add(x, 1); //x的次數加一 } for (int i = 0; i <= n - 1; ++i) printf("%lld\n", res[i]); return 0; }
acwing244謎同樣的牛
有n頭奶牛,已知它們的身高爲 1~n 且各不相同,但不知道每頭奶牛的具體身高。
如今這n頭奶牛站成一列,已知第i頭牛前面有Ai頭牛比它低,求每頭奶牛的身高。
1≤n≤105排序
/* 本題能夠從n往1向前看,若是他的前面有a[i]頭牛比他低,那第i頭牛的高度就是能夠選擇的身高的第i+1小。 所以能夠給每一個身高一個標值,1表示沒被選,0表示被選中。那麼尋找第a[i]+1個沒被選中的牛就能夠二分查找 前綴和等於a[i]+1的那個數字,樹狀數組維護這個前綴和便可 */ #include<bits/stdc++.h> using namespace std; int n; int const N = 1e5 + 10; int c[N], a[N], ans[N]; int lowbit(int x) { return x & -x; } void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { cin >> n; for (int i = 2; i <= n; ++i) scanf("%d", &a[i]); for (int i = 1; i <= n; ++i) c[i] = lowbit(i); // 初始化,由於每一個身高都沒備選,那麼都初始化爲1,Tr數組就是區間的長度,即爲lowbit(i) for (int i = n; i >= 1; --i) // 倒着往前看 { // 二分查找a[i]+1小 int l = 1, r = n; while (l < r) { int mid = (l + r) >> 1; if (query(mid) >= a[i] + 1) r = mid; else l = mid + 1; } ans[i] = l; // 記錄答案 add(l, -1); // 這個牛被選了,從1變成0 } for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]); return 0; }
acwing260買票
給定n,表示遊客的數目;隨後n行,每行兩個數,p[i]和v[i],分別表示當這個遊客插隊後,前面的人數以及這個遊客的編號。求出當所有遊客完成插隊後,隊列中的遊客編號狀況。
n ~ 2e5,P[i]、V[i] ~ short隊列
// 本題的思路和acwing244同樣,倒着往前看,插入前面人數+1的位置。 #include <bits/stdc++.h> using namespace std; int const N = 2e5 + 10; int res[N], P[N], V[N], n, c[N]; typedef long long LL; int lowbit(int x) { return x & -x; } void add(int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } int query(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { while (scanf("%d", &n) != EOF) { for (int i = 1; i <= n; ++i) { scanf("%d%d", &P[i], &V[i]); c[i] = lowbit(i); } for (int i = n; i >= 1; --i) { int pos = P[i] + 1; int l = 1, r = n; while (l < r) { int mid = l + r >> 1; if (query(mid) >= pos) r = mid; else l = mid + 1; } res[l] = V[i]; add(l, -1); } for (int i = 1; i <= n; ++i) printf("%d ", res[i]); cout << endl; } return 0; }
Vijos P1448校門外的樹
校門外有不少樹,有蘋果樹,香蕉樹,有會扔石頭的,有能夠吃掉補充體力的……現在學校決定在某個時刻在某一段種上一種樹,保證任一時刻不會出現兩段相同種類的樹,現有兩個操做:
K=1,讀入l、r表示在區間[l,r]中種上一種樹,每次操做種的樹的種類都不一樣
K=2,讀入l,r表示詢問l~r之間能見到多少種樹
(l,r>0)
校門長度n,詢問次數m<=50000
/* 左右括號的方法。在一個區間內種樹,至關於加一對括號。用樹狀數組維護從起始到這個位置的左右括號的數量。 區間內有左括號那麼必定有這一種類型的樹,只有離開了對應的右括號這種樹纔沒有了。 因此爲了統計區間[x,y]內的樹種類,只需把y左邊左括號的個數-(x-1)左邊右括號的個數便可。 */ #include <bits/stdc++.h> using namespace std; int const N = 5e4 + 10; int c1[N], c2[N], n, m; // c1[x]維護左括號的數目,c2[x]維護右括號的數目 int lowbit(int x) { return x & -x; } void add(int c[], int x, int y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } int query(int c[], int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; return res; } int main() { cin >> n >> m; for (int i = 1, op, l, r; i <= m; ++i) { scanf("%d%d%d", &op, &l, &r); if (op == 1) add(c1, l, 1), add(c2, r, 1); // l處左括號數目加一,r處右括號數目加一 else printf("%d\n", query(c1, r) - query(c2, l - 1)); // r的左括號數目-(l-1)的右括號數目 } return 0; }
POJ2155 Matrix
一個n*n的只含01二維矩陣,有m個操做,每一個操做兩種類型:
C x1 y1 x2 y2:把以(x1, y1)爲左上角,(x2, y2)爲右下角的矩陣的內的數所有反轉(0變1,1變0)
Q x y:查詢a[x][y]的數是多少
n、m~1e5
// 翻轉一個區間至關於區間的每一個數加1,最後是0仍是1就模2便可。 // 加1在模2的意義下就是把01反轉 #include <bits/stdc++.h> using namespace std; typedef long long LL; int const N = 1e3 + 10; LL c[N][N], n, m, op, T; // c[x][y]維護b[i][j]的前綴和 int lowbit(int x) { return x & (-x); } void add(int x, int y, LL z) { for (int i = x; i <= n; i += lowbit(i)) for (int j = y; j <= n; j += lowbit(j)) c[i][j] += z; } LL query(int x, int y) { LL res = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) res += c[i][j]; return res; } int main() { cin >> T; while (T--) { memset(c, 0, sizeof c); cin >> n >> m; for (int i = 1, x1, y1, x2, y2; i <= m; ++i) { char op[2]; scanf("%s", op); if (op[0] == 'C') { // 區間增長1 scanf("%d%d%d%d", &x1, &y1, &x2, &y2); add(x2 + 1, y2 + 1, 1); add(x1, y1, 1); add(x2 + 1, y1, -1); add(x1, y2 + 1, -1); } else { // 單點查詢 scanf("%d%d", &x1, &y1); printf("%lld\n", query(x1, y1) % 2); } } printf("\n"); } return 0; }
ACM-ICPC 2018 徐州賽區網絡預賽 H.Ryuji doesn't want to study
一開始給定n個數,m次詢問。每次詢問兩種類型:
1 x y, 表示詢問a[l]*len + a[l + 1] * (len - 1) + ... + a[r] * 1,len = r - l + 1。
2 x y, 表示把a[x]賦值爲y
n ~ 1e5, m ~ 1e5
題解:\(\sum_{i=l}^ra[i]*(r - i + 1) = (r+1)\sum_{i=l}^ra[i] - \sum_{i=l}^ri * a[i]\)
所以,能夠使用樹狀數組分別維護a[i]和i * a[i]的前綴和便可
#include<bits/stdc++.h> using namespace std; typedef long long LL; int n, m; int const N = 1e5 + 10; LL c1[N], c2[N], a[N], sum1[N], sum2[N]; // c1[x]維護ai的前綴和,c2[x]維護i*ai的前綴和 LL lowbit(LL x) { return x & -x; } // 單點修改 void add(LL c[], int x, LL y) { for (int i = x; i <= n; i += lowbit(i)) c[i] += y; // 不斷往父節點跳 } // 查詢Sx前綴和 LL query(int x, LL c[]) { LL res = 0; for (int i = x; i; i -= lowbit(i)) res += c[i]; // 不斷往前一個兄弟節點跳 return res; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); // 讀入a[i] sum1[i] = sum1[i - 1] + a[i]; sum2[i] = sum2[i - 1] + i * a[i]; } for (int i = 1; i <= n; ++i) { c1[i] = sum1[i] - sum1[i - lowbit(i)]; c2[i] = sum2[i] - sum2[i - lowbit(i)]; } while (m--) { LL op, x, y; scanf("%lld%lld%lld", &op, &x, &y); if (op == 1) { // 詢問 printf("%lld\n", (y + 1) * (query(y, c1) - query(x - 1, c1)) - (query(y, c2) - query(x - 1, c2))); } else { // 單點修改 add(c1, x, y - a[x]), add(c2, x, x * y - x * a[x]); a[x] = y; } } return 0; }