C[1] = C[0001] = A[1];
C[2] = C[0010] = A[1]+A[2];
C[3] = C[0011] = A[3];
C[4] = C[0100] = A[1]+A[2]+A[3]+A[4];
C[5] = C[0101] = A[5];
C[6] = C[0110] = A[5]+A[6];
C[7] = C[0111] = A[7];
C[8] = C[1000] = A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
即:
补码为原码取反后加1,如果将补码+1进位,那么最末尾的1和原码最右边的1一定是同一个位置
inline int lowbit(x){
return x&(-x);
}
void update(int *arr,int index,int diff,int len){
/*
* arr:树状数组
* index:待更新的位置
* diff:改变的值
* len:树状数组长度
*/
for(int i=index;i<=len;i+=lowbit(i))
arr[i] += diff;
}
int getsum(int *arr,int x){
/*
* arr:树状数组
* x:求从1到x的和
*/
int ans = 0;
for(int i=x;i;i-=lowbit(i))
ans += arr[i];
return ans;
}
#include
#include
#include
#include
#include
using namespace std;
int lowbit(const int t) {
return t & (-t);
}
void insert(int t, int d) {
while (t <= n){
a[t] += d;
t = t + lowbit(t);
}
}
int getSum(int t) {
int sum = 0;
while (t > 0){
sum += a[t];
t = t - lowbit(t);
}
return sum;
}
int main() {
int t, k, d;
scanf("%d", &t);
k= 1;
while (t--){
memset(a, 0, sizeof(a));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &d);
insert(i, d);
}
string str;
printf("Case %d:\n", k++);
while (cin >> str) {
if (str == "End") break;
int x, y;
scanf("%d %d", &x, &y);
if (str == "Query")
printf("%lld\n", getSum(y) - getSum(x - 1));
else if (str == "Add")
insert(x, y);
else if (str == "Sub")
insert(x, -y);
}
}
return 0;
}
#include
using namespace std;
#define MAXN 100
#define MAXIND 256 //线段树节点个数
//构建线段树,目的:得到M数组.
void build(int node, int b, int e, int M[], int A[])
{
if (b == e)
M[node] = b; //只有一个元素,只有一个下标
else
{
build(2 * node, b, (b + e) / 2, M, A);
build(2 * node + 1, (b + e) / 2 + 1, e, M, A);
if (A[M[2 * node]] <= A[M[2 * node + 1]])
M[node] = M[2 * node];
else
M[node] = M[2 * node + 1];
}
}
//找出区间 [i, j] 上的最小值的索引
int query(int node, int b, int e, int M[], int A[], int i, int j)
{
int p1, p2;
//查询区间和要求的区间没有交集
if (i > e || j < b)
return -1;
if (b >= i && e <= j)
return M[node];
p1 = query(2 * node, b, (b + e) / 2, M, A, i, j);
p2 = query(2 * node + 1, (b + e) / 2 + 1, e, M, A, i, j);
//return the position where the overall
//minimum is
if (p1 == -1)
return M[node] = p2;
if (p2 == -1)
return M[node] = p1;
if (A[p1] <= A[p2])
return M[node] = p1;
return M[node] = p2;
}
int main()
{
int M[MAXIND]; //下标1起才有意义,否则不是二叉树,保存下标编号节点对应区间最小值的下标.
memset(M,-1,sizeof(M));
int a[]={3,4,5,7,2,1,0,3,4,5};
build(1, 0, sizeof(a)/sizeof(a[0])-1, M, a);
cout<
#include
#include
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define root 1 , N , 1
#define LL long long
const int maxn = 111111;
LL add[maxn<<2];
LL sum[maxn<<2];
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
if (add[rt]) {
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += add[rt] * (m - (m >> 1));
sum[rt<<1|1] += add[rt] * (m >> 1);
add[rt] = 0;
}
}
void build(int l,int r,int rt) {
add[rt] = 0;
if (l == r) {
scanf("%lld",&sum[rt]);
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
if (L <= l && r <= R) {
add[rt] += c;
sum[rt] += (LL)c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
LL query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
LL ret = 0;
if (L <= m) ret += query(L , R , lson);
if (m < R) ret += query(L , R , rson);
return ret;
}
int main() {
int N , Q;
scanf("%d%d",&N,&Q);
build(root);
while (Q --) {
char op[2];
int a , b , c;
scanf("%s",op);
if (op[0] == 'Q') {
scanf("%d%d",&a,&b);
printf("%lld\n",query(a , b ,root));
} else {
scanf("%d%d%d",&a,&b,&c);
update(a , b , c , root);
}
}
return 0;
}
有数组A,数组A对应的前缀和数组为S,有:
S [ k ] = ∑ i = 0 k A [ i ] S[k]=\sum_{i=0}^{k}A[i] S[k]=i=0∑kA[i]
由此方便了范围查询:
∑ i = L R A [ i ] = S [ R ] − S [ L − 1 ] \sum_{i=L}^RA[i]=S[R]-S[L-1] i=L∑RA[i]=S[R]−S[L−1]
对于二维数组A,数组A对应的前缀和数组为S,有:
S [ i ] [ j ] = ∑ m = 0 i ∑ n = 0 j A [ i ] [ j ] S[i][j]=\sum_{m=0}^i\sum_{n=0}^jA[i][j] S[i][j]=m=0∑in=0∑jA[i][j]
计算时二维前缀和时,可以使用递推公式:
S [ i ] [ j ] = A [ i ] [ j ] + S [ i − 1 ] [ j ] + S [ i ] [ j − 1 ] − S [ i − 1 ] [ j − 1 ] S[i][j]=A[i][j]+S[i-1][j]+S[i][j-1]-S[i-1][j-1] S[i][j]=A[i][j]+S[i−1][j]+S[i][j−1]−S[i−1][j−1]
当求被点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)和点 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)围起来的元素的和时,可以使用:
∑ i = x 1 x 2 ∑ j = y 1 y 2 A [ i ] [ j ] = S [ x 2 ] [ y 2 ] − S [ x 1 − 1 ] [ y 2 ] − S [ x 2 ] [ y 1 − 1 ] + S [ x 1 − 1 ] [ y 1 − 1 ] \sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}A[i][j]=S[x_2][y_2]-S[x_1-1][y2]-S[x_2][y_1-1]+S[x_1-1][y_1-1] i=x1∑x2j=y1∑y2A[i][j]=S[x2][y2]−S[x1−1][y2]−S[x2][y1−1]+S[x1−1][y1−1]
差分可解决范围更新的问题。
思想:
样例:
#include
using namespace std;
const int maxn=1e5+9;
int a[maxn],b[maxn];
int main(){
int i,j,k,n,m,p;
cin>>n>>m;
for(i=1;i<=n;i++){
cin>>a[i];
}
for(i=1;i<=m;i++){
int L,R,t;
cin>>t>>L>>R>>p;
if(t==1){
b[L]+=p;b[R+1]-=p;
}
else{
b[L]-=p;b[R+1]+=p;
}
}
int add=0;
for(i=1;i<=n;i++){
add+=b[i];
a[i]+=a[i-1]+add;
}
int x,y;
cin>>x>>y;
cout<