樹狀數組初步

引入

樹狀數組用於求區間和,其修改和查詢的複雜度都是\(O(logn)\),很是好寫,比較小巧。node

幾種基礎用法,關於權值樹狀數組在另外一篇博客。c++

單點修改,區間查詢

區間和

HDU-1166 敵兵佈陣

模版:

#include <bits/stdc++.h>
#define N 50005
using namespace std;
typedef long long ll;
int max(int a, int b) {
    return a > b ? a : b;
}
int min(int a, int b) {
    return a < b ? a : b;
}
int d[N];
int n;
void update(int x, int v) {
    for (int i = x; i <= n; i += i & (-i))
        d[i] += v;
}
ll query(int x) {
    ll ans = 0;
    for (int i = x; i; i -= i & (-i))
        ans += d[i];
    return ans;
}
int main() {
    int t;
    scanf("%d", &t);
    int cnt = 0;
    while (t--) {
        memset(d, 0, sizeof(d));
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int v;
            scanf("%d", &v);
            update(i, v);
        }
        cnt++;
        printf("Case %d:\n", cnt);
        while (1) {
            char ch[10];
            scanf("%s", ch);
            int x, y;
            if (ch[0] == 'Q') {
                scanf("%d%d", &x, &y);
                printf("%lld\n", query(y) - query(x - 1));
            }
            else if (ch[0] == 'A') {
                scanf("%d%d", &x, &y);
                update(x, y);
            }
            else if (ch[0] == 'S') {
                scanf("%d%d", &x, &y);
                update(x, -y);
            }
            else break;
        }
    }
    return 0;
}

區間最小值

不建議使用樹狀數組求區間最小值,比較難以理解,並且寫起來並不簡單git

HDU-1754 I Hate It

模版

#include <cstdio>
#include <cstring>
#define N 200050
using namespace std;
int h[N], a[N];
int lowbit(int x) {
    return x & (-x);
}
int n, m;
int max(int a, int b) {return a > b ? a : b;}
void update(int x) {
    int lx;
    while (x <= n) {
        h[x] = a[x];
        lx = lowbit(x);
        for (int i = 1; i < lx; i <<= 1)
            h[x] = max(h[x], h[x - i]);
        x += lowbit(x);
    }
}
int query(int l, int r) {
    int ans = 0;
    while (r >= l) {
        ans = max(a[r], ans);
        r--;
        for (; r - lowbit(r) >= l; r -= lowbit(r))
            ans = max(h[r], ans);
    }
    return ans;
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        memset(h, 0, sizeof(h));
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            update(i);
        }
        while (m--) {
            char ch[2];
            int x, y;
            scanf("%s%d%d", ch, &x, &y);
            if (ch[0] == 'Q') {
                printf("%d\n", query(x, y));
            }
            else {
                a[x] = y;
                update(x);
            }
        }
    }
    return 0;
}

單點修改,矩陣求和

直接使用二維樹狀數組便可。數組

附:矩陣前綴和求一部分和的公式:spa

\(sumv(x2, y2) - sumv(x1 - 1, y2) - sumv(x2, y11 - 1) + sumv(x1 - 1, y11 - 1)\)code

\(sumv(x,y)\)爲從(1,1)到(x,y)的前綴和排序

題目:HihoCoder-1336 Matrix Sum

模版:

#include <cstdio>
#include <cstring>
#define p 1000000007
#define N 1050
using namespace std;
typedef long long ll;
ll d[N][N];
int n, m;
int lowbit(int x) {
    return x & (-x);
}
void update(int x, int y, ll v) {
    for (int i = x; i <= n; i += lowbit(i)) {
        for (int j = y; j <= n; j += lowbit(j)) {
            d[i][j] += v;
        }
    }
}
ll sumv(int x, int y) {
    ll ans = 0;
    for (int i = x; i; i -= lowbit(i)) {
        for (int j = y; j; j -= lowbit(j)) {
            ans += d[i][j];
        }
    }
    return ans;
}
ll query(int x1, int y11, int x2, int y2) {
    return sumv(x2, y2) - sumv(x1 - 1, y2) - sumv(x2, y11 - 1) + sumv(x1 - 1, y11 - 1);
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        char ch[10];
        scanf("%s", ch);
        if (ch[0] == 'A') {
            int x, y; ll v;
            scanf("%d%d%lld", &x, &y, &v);
            x++; y++;
            update(x, y, v);
        }
        else {
            int x1, y11, x2, y2;
            scanf("%d%d%d%d", &x1, &y11, &x2, &y2);
            x1++; y11++; x2++; y2++;
            printf("%lld\n", (query(x1, y11, x2, y2) + p) % p);
        }
    }
    return 0;
}

樹狀數組求逆序對

使用樹狀數組維護一個位置以前一共有了多少數,當第i個數a[i]加進來時,先update更新,在詢問這個數以前一共有了多少數,使用i-get(a[i])即爲對逆序對的貢獻,累計便可get

題目:洛谷-P1966 火柴排隊

此題題意是讓a數組和b數組之間每一個位置均對應爲相同的大小順序,問最少須要移動幾回,即把a的位置移動到b的位置須要移動多少次,排序以後將a映射到b求逆序對便可博客

模版

#include<bits/stdc++.h>
#define maxn 100060
#define p 99999997
using namespace std;
int c[maxn], d[maxn], n;
inline int getnum(){
    char c; int ans = 0; bool flag = false;
    while (!isdigit(c = getchar()) && c != '-');
    if (c == '-') flag = true; else ans = c - '0';
    while (isdigit(c = getchar())) ans = ans * 10 + c - '0';
    return ans * (flag ? -1 : 1);
}
struct node{
    long long v; int pos;
}a[maxn], b[maxn];
int cmp(node x, node y){
    return x.v < y.v;
}
inline int lowbit(int x){
    return x & (-x);
}
inline void updata(int x){
    while (x <= n){
        d[x]++;
        x += lowbit(x);
    }
}
inline int getsum(int x){
    int ans = 0;
    while (x > 0){
        ans += d[x] % p;
        x -= lowbit(x);
    }
    return ans;
}
int main(){
    n = getnum();
    for (int i = 1; i <= n; i++){
        a[i].v = getnum();
        a[i].pos = i;
    }
    for (int i = 1; i <= n; i++){
        b[i].v = getnum();
        b[i].pos = i;
    }
    sort(a + 1, a + n + 1, cmp);
    sort(b + 1, b + n + 1, cmp);
    for (int i = 1; i <= n; i++){
        c[b[i].pos] = a[i].pos;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++){
        updata(c[i]);
        ans += i - getsum(c[i]);
        ans %= p;
    }
    printf("%d", ans);
    return 0;
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息