树状数组,题解,Java(5)

引入:树状数组

树状数组,题解,Java(5)_第1张图片

  自我感悟
 1.树状数组奇数为第0阶梯,偶数却不为2的m次方的形式为第1阶梯,偶数为2的m次方的形式为第m阶梯,
 2.可以根据目的对阶梯性质进行定义,如:和,最大值...,
 3.阶梯的覆盖优先顺序为右方高阶梯覆盖左方低阶梯,且每个结点只有一个父结点,
 4.低阶梯到高阶梯的方式为+x&(-x)

A - 敌兵布阵
线段树模板题:执行操作单点加减,区间求和
 

#include 
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int w[N];
struct node {
    int l, r;
    int sum;
}t[N << 2];
void pushup(int x) { 
    t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum;
}
void build(int l, int r, int x = 1) {
    t[x] = { l, r, w[l] };
    if (l == r) return;
    int mid = l + r >> 1;                                  //x|1表示奇数则本身,偶数则+1
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);   //按位或运算符则是将两个数字的二进制值的每一位进行或运算。
    pushup(x);
}
void modify(int a, int c, int x = 1) {
    if (a == t[x].l && a == t[x].r) { t[x].sum += c; return; }
    int mid = t[x].l + t[x].r >> 1;
    modify(a, c, x << 1 | (a > mid));   //a>mid为1,奇数则本身,偶数则+1,a= t[x].r) return t[x].sum;          //囊括全部
    int mid = t[x].l + t[x].r >> 1;
    int res = 0;
    if (l <= mid) res += ask(l, r, x << 1);
    if (r > mid) res += ask(l, r, x << 1 | 1);
    return res;
}
int main()
{
    int t; cin >> t;
    for (int T = 1; T <= t;T++ ) {
        printf("Case %d:\n", T);
        int n; 
        scanf("%d", &n);
        for(int i=1;i<=n;i++)
            scanf("%d", &w[i]);
        build(1, n);
        char s[10];
        while (scanf("%s", s)) {
            if (s[0] == 'E')
                break;
            int x, y; 
            scanf("%d %d", &x, &y);
            if (s[0] == 'A') 
                modify(x, y);
            else if (s[0] == 'S') 
                modify(x, -y);
            else if (s[0] == 'Q') 
                printf("%d\n", ask(x, y));
        }
    }
    return 0;
}

树状数组解法

#include 
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int t[N];             //树状数组
int n, m;
int lowbit(int x) { 
    return x & -x; 
}
void add(int x, int c) { 
    for (int i = x; i <= n; i += lowbit(i))   //举例:以1开始,1,2,4,8..;2开始,2,4,8
        t[i] += c; 
}
int ask(int x) {                 //举例:7->6->4
    int res = 0;
    for (int i = x; i; i -= lowbit(i))   
        res += t[i];
    return res;
}
int main()
{
    int tt; 
    cin >> tt;
    for (int T = 1; T <= tt;T++) {
        printf("Case %d:\n", T);
        scanf("%d", &n);
        for(int i=1;i<=n;i++)
            t[i] = 0; //初始化
        for (int i = 1; i <= n; i++) { 
            int x; 
            scanf("%d", &x); 
            add(i, x); 
        }
        while (true) {
            char s[10]; scanf("%s", s);
            if (s[0] == 'E') 
                break;
            int a, b; 
            scanf("%d %d", &a, &b);
            if (s[0] == 'A') 
                add(a, b);
            else if (s[0] == 'S') 
                add(a, -b);
            else if (s[0] == 'Q') 
                printf("%d\n", ask(b) - ask(a - 1));
        }
    }
    return 0;
}

B - I Hate It
 思路:树状数组
 执行操作:单点修改,区间求最大值
a树状数组记录原素组,b树状数组记录最大值,单点修改,区间求最大值自需向高阶梯递进,枚举低阶梯即可

#include
#include
#include
#define Z 200005
using namespace std;
int n, m, op1, op2;
char ch[5];
int c[Z], a[Z];
int lowbit(int x){
    return x & (-x);
}
int geta(int l, int r){           
    int temp;
    int ans = 0;
    while (r >= l){
        ans = max(a[r], ans);
        r--;
        for (; r - lowbit(r) >= l; r -= lowbit(r)){          //向低阶梯
            ans = max(ans, c[r]);
        }
    }
    return ans;
}
void update(int x){                  //更新最大值
    int temp;
    while (x <= n){
        temp = lowbit(x);      //奇数1,0阶梯,不需比较
        c[x] = a[x];
        for (int i = 1; i < temp; i <<= 1)       //假设temp:8,则枚举了7,6,4,三个高阶梯    
            c[x] = max(c[x], c[x - i]);
        x += temp;           //向高阶梯
    }
    return;
}
void up(int x, int i){          //每读入一个数,原来的最大值不变,或者最大值变为a[i],没必要枚举下面子节点
    int temp;
    while (x <= n && a[i] > c[x]){
        temp = lowbit(x);
        c[x] = a[i]; 
        x += temp;
    }
    return;
}
int main(){
    while (scanf("%d%d", &n, &m) != EOF){
        memset(c, 0, sizeof(c));                     //记录区间最大值
        for (int i = 1; i <= n; i++){
            scanf("%d", &a[i]);                  //原数组
            up(i, i);
        }
        for (int i = 1; i <= m; i++){
            scanf("%s%d%d", ch, &op1, &op2);
            if (ch[0] == 'Q') 
                printf("%d\n", geta(op1, op2));
            else{
                a[op1] = op2;
                update(op1);
            }
        }
    }
    return 0;
}

C - A Simple Problem with Integers
执行操作:区间加减,区间求和
在区间内前缀和增加x*(i-l+1)=x*i-x*(l-1),维护前缀和树状数组和加法树状数组即可,
通过加减实现在[l,r]实现维护,最后再两个数组在[l.r]相加即可

#include
#include
using namespace std;
typedef long long ll;
#define MAX 100005
int n, q;
int a[MAX];
ll bit0[MAX], bit1[MAX];         //定义bit0前缀和树状数组,bit1加法树状数组
void updata(ll* b, int i, int val){
    while (i <= n){
        b[i] += val;
        i += (i & -i);              //向高阶梯
    }
}
ll query(ll* b, int i){
    ll res = 0;
    while (i > 0){
        res += b[i];
        i -= (i & -i);                //向低阶梯
    }
    return res;
}
int main()
{
    ios_base::sync_with_stdio(0);
    while (cin >> n >> q){
        memset(bit0, 0, sizeof(bit0));
        memset(bit1, 0, sizeof(bit1));
        for (int i = 1; i <= n; i++){
            cin >> a[i];
            updata(bit0, i, a[i]);
        }
        while (q--){
            int l, r, x;
            char ch;
            cin >> ch;
            cin >> l >> r;
            if (ch == 'C'){           //在[l,r]进行操作
                cin >> x;
                updata(bit0, l, -x * (l - 1));
                updata(bit1, l, x);
                updata(bit0, r + 1, x * r);
                updata(bit1, r + 1, -x);
            }
            else{            //在[l,r]进行操作
                ll sum = 0;
                sum += query(bit0, r) + query(bit1, r) * r;
                sum -= query(bit0, l - 1) + query(bit1, l - 1) * (l - 1);
                cout << sum << endl;
            }
        }
    }
}


D-just a Hook

引入:懒标记
操作:区间修改,输出[1,n]和
 

#include 
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct node {
    int l, r;
    int val; //区间和
    int lazy;                //懒标记
}t[N << 2];
void pushdown(node& op, int lazy) {
    op.val = lazy * (op.r - op.l + 1);
    op.lazy = lazy;
}
void Pushdown(int x) {                
    if (!t[x].lazy) return;
    pushdown(t[x << 1], t[x].lazy);
    pushdown(t[x << 1 | 1], t[x].lazy);
    t[x].lazy = 0;
}
void pushup(int x) { 
    t[x].val = t[x << 1].val + t[x << 1 | 1].val; 
}
void build(int l, int r, int x = 1) {
    t[x] = { l, r, 1, 0 };
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
    pushup(x);
}
void modify(int l, int r, int c, int x = 1) {
    if (l <= t[x].l && r >= t[x].r) { 
        pushdown(t[x], c); 
        return; 
    }
    Pushdown(x);
    int mid = t[x].l + t[x].r >> 1;
    if (l <= mid) modify(l, r, c, x << 1);
    if (r > mid) modify(l, r, c, x << 1 | 1);
    pushup(x);
}
int main()
{
    int T; 
    cin >> T;
    for(int i=1;i<=T;i++) {
        int n, m; 
        scanf("%d%d", &n, &m);
        build(1, n);
        while (m--) {
            int l, r, c; 
            scanf("%d%d%d", &l, &r, &c);
            modify(l, r, c);
        }
        printf("Case %d: The total value of the hook is %d.\n", i, t[1].val);
    }
    return 0;
}

java(5)

树状数组,题解,Java(5)_第2张图片

 

 

你可能感兴趣的:(数据结构,算法,servlet)