1.单点更新&前缀和查询(->区间查询)
int tr[maxn];//树状数组
void add(int tr[],int x,int k)
{
while(x < maxn){
tr[x] += k;
x += (x & -x);
}
}
int query(int tr[],int x){
int ans =0 ;
while(x){
ans += tr[x];
x -= (x & -x);
}
return ans;
}
2.区间更新&区间查询
区间更新这里引进了一个数组delta数组,delta[i]表示区间 [i, n] 的共同增量,每次你需要更新的时候只需要更新delta数组就行了,因为每段区间更新的数都记录在这个数组中。
sum[i]=a[1]+a[2]+a[3]+......+a[i]+delta[1]*(i-0)+delta[2]*(i-1)+delta[3]*(i-2)+......+delta[i]*(i-i+1); = sigma( a[x] ) + sigma( delta[x] * (i + 1 - x) ) = sigma( a[x] ) + (i + 1) * sigma( delta[x] ) - sigma( delta[x] * x )
ll A[maxn];//A[i]表示原数组
ll delta[maxn];//delta[i] 表示A[i,n]共同的增量
ll xdelta[maxn];//xdelta[i] = i * delta[i]
void add(int x,int key,ll arr[])
{
while(x < maxn){
arr[x] += key;
x += (x & -x);
}
}
ll query(int x,ll arr[])
{
ll ans = 0;
while (x) {
ans += arr[x];
x -= (x & -x);
}
return ans;
}
//step1:A数组记录初值
for (int i = 1; i <= n; i++) {
scanf("%d",&t);
update(i,t,A);
}
//step2:要在区间[a,b]加上c,需要更新delta和xdelta数组
if(op == 'C'){
scanf("%d%d%d",&a,&b,&c);
update(a, c, delta);
update(b + 1, -c, delta);
update(a, a * c, xdelta);
update(b + 1, -c * (b + 1), xdelta);
}
//step3:区间查询[a,b],需要统计A,delta,xdelta数组
if(op == 'Q'){
scanf("%d%d",&a,&b);
ans1 = sum(b, A) + (b + 1) * sum(b, delta) - sum(b, xdelta);
if(a > 1) ans2 = sum(a - 1, A) + a * sum(a - 1, delta) - sum(a - 1, xdelta);
else ans2 = 0;
printf("%lld\n",ans1 - ans2);
}
3.区间更新&单点查询
如果查询时仅需要单点查询,那么可以使用差分。
ans[x] = A[x] + delta[x] + delta[x - 1] + ... + delta[1] //delta数组的前缀和
= A[x] + query(delta,x)
ll A[maxn];//A表示原数组
ll delta[maxn];//delta[i]表示[i,n]的共同增量
void add(ll tr[],int x,ll k){
while(x < maxn) {
tr[x] += k;
x += (x & -x);
}
}
ll query(ll tr[],int x){
ll ans = 0;
while(x){
ans += tr[x];
x -= (x & -x);
}
return ans;
}
//区间更新,在区间[a,b]加上c
if(op == 'C'){
add(delta,a,c);
add(delta,b + 1,-c);
}
//单点查询,查询x的值
if(op == 'Q'){
ll ans = query(delta,x) + A[x];
}