单点修改,区间查询:
给定数列 a1,a2,…,ana1,a2,…,an,你需要依次进行 qq 个操作,操作有两类:
1 i x
:给定 i,xi,x,将 aiai 加上 xx;2 l r
:给定 l,rl,r,求 ∑ri=lai∑i=lrai 的值(换言之,求 al+al+1+⋯+aral+al+1+⋯+ar 的值)。
输入格式
第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1≤n,q≤1061≤n,q≤106。
第二行 nn 个整数 a1,a2,…,ana1,a2,…,an,表示初始数列。保证 |ai|≤106|ai|≤106。
接下来 qq 行,每行一个操作,为以下两种之一:
1 i x
:给定 i,xi,x,将 a[i]a[i] 加上 xx;2 l r
:给定 l,rl,r,求 ∑ri=lai∑i=lrai 的值。
保证 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
输出格式
对于每个 2 l r
操作输出一行,每行有一个整数,表示所求的结果。
样例
Input | Output |
---|---|
3 2 1 2 3 1 2 0 2 1 3 | 6 |
数据范围与提示
对于所有数据,1≤n,q≤106,1≤n,q≤106, |ai|≤106|ai|≤106, 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
#include<bits/stdc++.h>;
using namespace std;
typedef long long ll;
const int N=1e6+6;
ll a[N],n;
void add(int x,int y)
{
for(int i=x; i<=n; i+=(i&-i))
a[i]+=y;
}
ll sum1(int x)
{
ll s=0;
for(int i=x; i>=1; i-=(i&-i))
s+=a[i];
return s;
}
int main()
{
int i,j,k;
int m,x,y,t;
cin>>n>>m;
for(i=1; i<=n; i++)
{
cin>>x;
add(i,x);
}
while(m--)
{
cin>>t>>x>>y;
if(t==1)
add(x,y);
else
printf("%lld\n",sum1(y)-sum1(x-1));
}
return 0;
}
区间修改,单点查询
给定数列 a[1],a[2],…,a[n]a[1],a[2],…,a[n],你需要依次进行 qq 个操作,操作有两类:
1 l r x
:给定 l,r,xl,r,x,对于所有 i∈[l,r]i∈[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l],a[l+1],…,a[r]a[l],a[l+1],…,a[r] 分别加上 xx);2 i
:给定 ii,求 a[i]a[i] 的值。
输入格式
第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1≤n,q≤1061≤n,q≤106。
第二行 nn 个整数 a[1],a[2],…,a[n]a[1],a[2],…,a[n],表示初始数列。保证 |a[i]|≤106|a[i]|≤106。
接下来 qq 行,每行一个操作,为以下两种之一:
1 l r x
:对于所有 i∈[l,r]i∈[l,r],将 a[i]a[i] 加上 xx;2 i
:给定 ii,求 a[i]a[i] 的值。
保证 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
输出格式
对于每个 2 i
操作,输出一行,每行有一个整数,表示所求的结果。
样例
Input | Output |
---|---|
3 2 1 2 3 1 1 3 0 2 2 | 2 |
数据范围与提示
对于所有数据,1≤n,q≤106,1≤n,q≤106, |a[i]|≤106|a[i]|≤106, 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn =1e6+6;
ll n,m,k;
ll a[maxn];
void add(int x,int s)
{
for(int i=x;i<=n;i+=(i&-i))
a[i]+=s;
}
ll sum1(int x)
{
ll s=0;
for(int i=x;i>=1;i-=(i&-i))
s+=a[i];
return s;
}
int main()
{
int i,j,k;
int y,t,x,m,z;
cin>>n>>m;
y=0;
for(i=1; i<=n; i++)
{
cin>>x;
add(i,x-y);
y=x;
}
while(m--)
{
cin>>t;
if(t==1)
{
cin>>x>>y>>z;
add(x,z);
add(y+1,-z);
}
else
{
cin>>x;
printf("%lld\n",sum1(x));
}
}
return 0;
}
区间修改,区间查询
给定数列 a[1],a[2],…,a[n]a[1],a[2],…,a[n],你需要依次进行 qq 个操作,操作有两类:
1 l r x
:给定 l,r,xl,r,x,对于所有 i∈[l,r]i∈[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l],a[l+1],…,a[r]a[l],a[l+1],…,a[r] 分别加上 xx);2 l r
:给定 l,rl,r,求 ∑ri=la[i]∑i=lra[i] 的值(换言之,求 a[l]+a[l+1]+⋯+a[r]a[l]+a[l+1]+⋯+a[r] 的值)。
输入格式
第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1≤n,q≤1061≤n,q≤106。
第二行 nn 个整数 a[1],a[2],…,a[n]a[1],a[2],…,a[n],表示初始数列。保证 |a[i]|≤106|a[i]|≤106。
接下来 qq 行,每行一个操作,为以下两种之一:
1 l r x
:对于所有 i∈[l,r]i∈[l,r],将 a[i]a[i] 加上 xx;2 l r
:输出 ∑ri=la[i]∑i=lra[i] 的值。
保证 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
输出格式
对于每个 2 l r
操作,输出一行,每行有一个整数,表示所求的结果。
样例
Input | Output |
---|---|
5 10 2 6 6 1 1 2 1 4 1 2 5 10 2 1 3 2 2 3 1 2 2 8 1 2 3 7 1 4 4 10 2 1 2 1 4 5 6 2 3 4 | 15 34 32 33 50 |
数据范围与提示
对于所有数据,1≤n,q≤106,1≤n,q≤106, |a[i]|≤106|a[i]|≤106, 1≤l≤r≤n,1≤l≤r≤n, |x|≤106|x|≤106。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+6;
ll c1[N],c2[N],n;
void add(ll x,ll w)
{
ll t=x;
while(x<=n)
{
c1[x]+=w;
c2[x]+=t*w;
x+=(x&-x);
}
return ;
}
ll sum(ll x)
{
ll s=0,t=x;
while(x>0)
{
s+=c1[x]*(t+1)-c2[x];
x-=(x&-x);
}
return s;
}
int main()
{
int i,j,k;
ll x,y=0,z,t,s1,s2,m;
cin>>n>>m;
for(i=1; i<=n; i++)
{
cin>>x;
add(i,x-y);
y=x;
}
while(m--)
{
cin>>t;
if(t==1)
{
cin>>x>>y>>z;
add(x,z);
add(y+1,-z);
}
else
{
cin>>x>>y;
printf("%lld\n",sum(y)-sum(x-1));
}
}
return 0;
}
‘&’的运用,二进制思想
三种树状数组模板对区间问题解决区别:建树方式
区间修改:差分思想
区间查询:差分,数组维护
第三种中模板数组维护,解决思想来自数学公式证明,可参考下面链接
树状数组证明链接:
https://blog.youkuaiyun.com/bestsort/article/details/80796531