问题
TLE代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int b[N];
void add(int l, int r, int d)
{b[r+1] -= d;b[l] += d;
}
int query(int x)
{int retval = 0;for(int i = 1; i <= x; i++){retval += b[i];}return retval;
}
int main()
{int n, m;scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++){int tmp;scanf("%d", &tmp);add(i, i, tmp);}for(int i = 1; i <= m; i++){char op;scanf(" %c", &op);if(op == 'C'){int l, r, d;scanf("%d%d%d", &l, &r, &d);add(l, r, d);}else if(op == 'Q'){int x;scanf("%d", &x);printf("%d\n", query(x));}}return 0;
}
假设n=1e5, m=1e5, 操作全是Q,则操作次数达到
1 0 10 10^{10} 1010
正确代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int b[N];
int n, m;
int lowbit(int x)
{return x & (-x);
}
void update(int x, int d)
{for(; x <= n; x += lowbit(x)){b[x] += d;}
}
int query(int x)
{int retval = 0;for(; x >= 1; x -= lowbit(x)){retval += b[x];}return retval;
}
int add(int l, int r, int d)
{update(l, d);update(r+1, -d);
}
int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; i++){int tmp;scanf("%d", &tmp);add(i, i, tmp);}for(int i = 1; i <= m; i++){char op;scanf(" %c", &op);if(op == 'C'){int l, r, d;scanf("%d%d%d", &l, &r, &d);add(l, r, d);}else if(op == 'Q'){int x;scanf("%d", &x);printf("%d\n", query(x));}}return 0;
}
思考
对比前缀和、差分和树状数组
算法 | 目的 | 单次操作复杂度 |
---|---|---|
前缀和 | 快速求子段和 | 区间求和 O ( 1 ) O(1) O(1) \; 区间修改 O ( n ) O(n) O(n) |
差分 | 快速子段修改 | 区间修改 O ( 1 ) O(1) O(1) \; 单点查询 O ( n ) O(n) O(n) |
树状数组 | 均衡上述目的,同时是动态版前缀和 | 单点修改 O ( l o g n ) O(logn) O(logn) \; 区间求和 O ( l o g n ) O(logn) O(logn) |
差分+树状数组 | 均衡差分 | \; 单点查询 O ( l o g n ) O(logn) O(logn) |
区间修改指的是区间加减
能够区间求和就自然可以单点查询
差分+树状数组的思路:树状数组维护差分序列,通过树状数组的单点修改操作进行差分的区间修改,通过树状数组的区间求和优化差分的单点查询
总结树状数组
求区间和要想到 (1)
单点修改要想到 (3)