这三题均可以用树状数组、分块或线段树来做

#130. 树状数组 1 :单点修改,区间查询

题目链接:https://loj.ac/problem/130

题目描述

这是一道模板题。

给定数列 a[1], a[2], \dots, a[n]a[1],a[2],…,a[n],你需要依次进行 qq 个操作,操作有两类:

  • 1 i x:给定 i,xi,x,将 a[i]a[i] 加上 xx;
  • 2 l r:给定 l,rl,r,求 \sum_{i=l}^ra[i]∑i=lr​a[i] 的值(换言之,求 a[l]+a[l+1]+\dots+a[r]a[l]+a[l+1]+⋯+a[r] 的值)

输入格式

第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1\le n,q\le 10^61≤n,q≤106。
第二行 nn 个整数 a[1], a[2], \dots, a[n]a[1],a[2],…,a[n],表示初始数列。保证 |a[i]|\le 10^6∣a[i]∣≤106。
接下来 qq 行,每行一个操作,为以下两种之一:

  • 1 i x:给定 i,xi,x,将 a[i]a[i] 加上 xx;
  • 2 l r:给定 l,rl,r,求 \sum_{i=l}^ra[i]∑i=lr​a[i] 的值。

保证 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

输出格式

对于每个 2 l r 操作输出一行,每行有一个整数,表示所求的结果。

样例

样例输入

3 2
1 2 3
1 2 0
2 1 3

样例输出

6

数据范围与提示

对于所有数据,1\le n,q\le 10^6,1≤n,q≤106, |a[i]|\le 10^6∣a[i]∣≤106, 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

树状数组解法:

思路:板子

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6;
int n,m;
ll sum[maxn];
int lowbit(int x){return x&(-x);}
void add(int x,ll val){
while(x<=maxn){
sum[x]+=val;
x+=lowbit(x);
}
}
ll getsum(int x){
ll res=;
while(x){
res+=sum[x];
x-=lowbit(x);
}
return res;
}
int main(){
scanf("%d%d",&n,&m);
ll c;
for(int i=;i<=n;i++){
scanf("%lld",&c);
add(i,c);
}
while(m--){
int id,x,y;
scanf("%d%d%d",&id,&x,&y);
if(id==)add(x,y);
else
cout<<getsum(y)-getsum(x-)<<endl;
}
return ;
}

分块解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
ll a[maxn],sum[maxn];
int n,q,num,block,l[maxn],r[maxn],belong[maxn];
void build(){
block=sqrt(n);
num=n/block;
if(n%block)num++;
for(int i=;i<=num;i++)
l[i]=(i-)*block+,r[i]=i*block;
r[num]=n;
for(int i=;i<=n;i++)
belong[i]=(i-)/block+; for(int i=;i<=num;i++)
for(int j=l[i];j<=r[i];j++)
sum[i]+=a[j];
}
ll query(int x,int y){
ll res=;
if(belong[x]==belong[y]){
for(int i=x;i<=y;i++)
res+=a[i];
return res;
}
for(int i=x;i<=r[belong[x]];i++) res+=a[i];
for(int i=belong[x]+;i<belong[y];i++)res+=sum[i];
for(int i=l[belong[y]];i<=y;i++)res+=a[i];
return res;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build();
while(q--){
int id,x,y;
scanf("%d%d%d",&id,&x,&y);
if(id==){
a[x]+=y;
sum[belong[x]]+=y;
}
else
printf("%lld\n",query(x,y));
}
return ;
}

线段树解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
int n,q;
ll tree[maxn*],a[maxn];
void pushup(int rt){
tree[rt]=tree[rt<<]+tree[rt<<|];
}
void build(int l,int r,int rt){
if(l==r){
tree[rt]=a[l];
return;
}
int mid=(l+r)>>;
build(l,mid,rt<<);
build(mid+,r,rt<<|);
pushup(rt);
}
void update(int pos,ll val,int l,int r,int rt){
if(l==r){
tree[rt]+=val;
return;
}
int mid=(l+r)>>;
if(pos<=mid) update(pos,val,l,mid,rt<<);
else update(pos,val,mid+,r,rt<<|);
pushup(rt);
}
ll ask(int L,int R,int l,int r,int rt){
ll res=;
if(l>=L&&r<=R) return tree[rt];
pushup(rt);
int mid=(l+r)>>;
if(L<=mid)res+=ask(L,R,l,mid,rt<<);
if(R>mid)res+=ask(L,R,mid+,r,rt<<|);
return res;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build(,n,);
while(q--){
int id,x,y;
scanf("%d%d%d",&id,&x,&y);
if(id==) update(x,y,,n,);
else printf("%lld\n",ask(x,y,,n,));
}
return ;
}

#131. 树状数组 2 :区间修改,单点查询

题目链接:https://loj.ac/problem/131

题目描述

这是一道模板题。

给定数列 a[1], a[2], \dots, a[n]a[1],a[2],…,a[n],你需要依次进行 qq 个操作,操作有两类:

  • 1 l r x:给定 l,r,xl,r,x,对于所有 i\in[l,r]i∈[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l], a[l+1], \dots, a[r]a[l],a[l+1],…,a[r] 分别加上 xx);
  • 2 i:给定 ii,求 a[i]a[i] 的值。

输入格式

第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1\le n,q\le 10^61≤n,q≤106。
第二行 nn 个整数 a[1], a[2], \dots, a[n]a[1],a[2],…,a[n],表示初始数列。保证 |a[i]|\le 10^6∣a[i]∣≤106。
接下来 qq 行,每行一个操作,为以下两种之一:

  • 1 l r x:对于所有 i\in[l,r]i∈[l,r],将 a[i]a[i] 加上 xx;
  • 2 i:给定 ii,求 a[i]a[i] 的值。

保证 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

输出格式

对于每个 2 i 操作,输出一行,每行有一个整数,表示所求的结果。

样例

样例输入

3 2
1 2 3
1 1 3 0
2 2

样例输出

2

数据范围与提示

对于所有数据,1\le n,q\le 10^6,1≤n,q≤106, |a[i]|\le 10^6∣a[i]∣≤106, 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

树状数组解法:

思路:这里需要用到差分数组,我们定义sum【i】为第i个数与第i-1个数的差,即sum【i】=a【i】-a【i-1】,这就使得a【i】=sum【1】+sum【2】+……sum【i】,就是sum数组的前缀和了,我们用数组数组维护sum数组的前缀和就好了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
ll sum[maxn];
int n,q;
int lowbit(int x){return x&(-x);}
void add(int x,int val){
while(x<=n){
sum[x]+=val;
x+=lowbit(x);
}
}
ll ask(int x){
ll res=;
while(x){
res+=sum[x];
x-=lowbit(x);
}
return res;
}
int main(){
cin>>n>>q;
ll tmp=;
for(int i=;i<=n;i++){
int x;
cin>>x;
add(i,x-tmp);
tmp=x;
}
while(q--){
int id,l,r,x;
cin>>id;
if(id==){
cin>>l>>r>>x;
add(l,x); add(r+,-x);
}
else{
cin>>x;
cout<<ask(x)<<endl;
}
}
return ;
}

分块解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
int n,q,block,num;
ll a[maxn],lazy[maxn],l[maxn],r[maxn],belong[maxn];
void build(){
block=sqrt(n);
num=n/block;
if(n%block)num++;
for(int i=;i<=num;i++)
l[i]=(i-)*block+,r[i]=block*i;
for(int i=;i<=n;i++) belong[i]=(i-)/block+;
}
void update(int x,int y,int z){
if(belong[x]==belong[y]){
for(int i=x;i<=y;i++)a[i]+=z;
return;
}
for(int i=x;i<=r[belong[x]];i++)a[i]+=z;
for(int i=belong[x]+;i<belong[y];i++)lazy[i]+=z;
for(int i=l[belong[y]];i<=y;i++)a[i]+=z;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build();
while(q--){
int id,x,y,z;
scanf("%d",&id);
if(id==){
scanf("%d%d%d",&x,&y,&z);
update(x,y,z);
}
else{
scanf("%d",&x);
printf("%lld\n",a[x]+lazy[belong[x]]);
}
}
return ;
}

线段树解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
int n,q;
ll a[maxn],tree[maxn*],lazy[maxn*];
void pushup(int rt){tree[rt]=tree[rt<<]+tree[rt<<|];}
void pushdown(int l,int r,int rt){
if(lazy[rt]){
tree[rt<<]+=lazy[rt]*l;
tree[rt<<|]+=lazy[rt]*r;
lazy[rt<<]+=lazy[rt];
lazy[rt<<|]+=lazy[rt];
lazy[rt]=;
}
}
void build(int l,int r,int rt){
if(l==r){
tree[rt]=a[l];
return;
}
int mid=l+r>>;
build(l,mid,rt<<);
build(mid+,r,rt<<|);
pushup(rt);
}
void update(int L,int R,int val,int l,int r,int rt){
if(l>=L&&r<=R){
tree[rt]+=val*(r-l+);
lazy[rt]+=val;
return ;
}
int mid=l+r>>;
pushdown(mid-l+,r-mid,rt);
if(mid>=L) update(L,R,val,l,mid,rt<<);
if(mid<R) update(L,R,val,mid+,r,rt<<|);
pushup(rt);
}
ll ask(int pos,int l,int r,int rt){
if(l==pos&&l==r)
return tree[rt];
int mid=l+r>>;
pushdown(mid-l+,r-mid,rt);
ll ans;
if(pos<=mid) return ask(pos,l,mid,rt<<);
else return ask(pos,mid+,r,rt<<|);
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build(,n,);
while(q--){
int id,x,y,z;
scanf("%d",&id);
if(id==){
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,,n,);
}
else{
scanf("%d",&x);
printf("%lld\n",ask(x,,n,));
}
}
return ;
}

#132. 树状数组 3 :区间修改,区间查询

题目链接:https://loj.ac/problem/132

题目描述

这是一道模板题。

给定数列 a[1], a[2], \dots, a[n]a[1],a[2],…,a[n],你需要依次进行 qq 个操作,操作有两类:

  • 1 l r x:给定 l,r,xl,r,x,对于所有 i\in[l,r]i∈[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l], a[l+1], \dots, a[r]a[l],a[l+1],…,a[r] 分别加上 xx);
  • 2 l r:给定 l,rl,r,求 \sum_{i=l}^ra[i]∑i=lr​a[i] 的值(换言之,求 a[l]+a[l+1]+\dots+a[r]a[l]+a[l+1]+⋯+a[r] 的值)。

输入格式

第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1\le n,q\le 10^61≤n,q≤106。
第二行 nn 个整数 a[1],a[2],\dots,a[n]a[1],a[2],…,a[n],表示初始数列。保证 |a[i]|\le 10^6∣a[i]∣≤106。
接下来 qq 行,每行一个操作,为以下两种之一:

  • 1 l r x:对于所有 i\in[l,r]i∈[l,r],将 a[i]a[i] 加上 xx;
  • 2 l r:输出 \sum_{i=l}^ra[i]∑i=lr​a[i] 的值。

保证 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

输出格式

对于每个 2 l r 操作,输出一行,每行有一个整数,表示所求的结果。

样例

样例输入

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\le n,q\le 10^6,1≤n,q≤106, |a[i]|\le 10^6∣a[i]∣≤106, 1\le l\le r\le n,1≤l≤r≤n, |x|\le 10^6∣x∣≤106。

树状数组解法:

思路:与上一题差不多,我们继续用一个数组sum1【i】存第i个数与第i-1个数的差,即

sum1【x】=a【i】-a【x-1】,a【x】=sum1【1】+sum1【2】+……sum1【x】

我们也可以很容易得出:

a【1】+a【2】+……a【x】=sum1【1】+(sum1【1】+sum1【2】)+……(sum1【1】+sum1【2】+……sum1【x】)

=x*sum1【1】+(x-1)*sum1【2】+……sum1【x】

=x*(sum1【1】+sum1【2】+……sum1【x】)-∑(i=1-x)(i-1)*sum1[i]

,所以我们就多建立一个数组sum2用来维护(x-1)sum1【x】就好了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
ll sum1[maxn],sum2[maxn],a[maxn];
int n,q;
int lowbit(int x){return x&(-x);}
void add(int x,ll val){
for(int i=x;i<=n;i+=lowbit(i)){
sum1[i]+=val;
sum2[i]+=(x-)*val;
}
}
ll ask(int x){
ll res=;
for(int i=x;i;i-=lowbit(i)){
res+=x*sum1[i]-sum2[i];
}
return res;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++){
scanf("%lld",&a[i]);
add(i,a[i]-a[i-]);
}
while(q--){
int id,l,r,x;
scanf("%d",&id);
if(id==){
scanf("%d%d%d",&l,&r,&x);
add(l,x); add(r+,-x);
}
else{
scanf("%d%d",&l,&r);
printf("%lld\n",ask(r)-ask(l-));
}
}
return ;
}

分块解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll maxn=1e6+;
int n,q,num,block;
ll lazy[maxn],sum[maxn],a[maxn],l[maxn],r[maxn],belong[maxn];
void build(){
block=sqrt(n);
num=n/block;
if(n%block)num++;
for(int i=;i<=num;i++)
l[i]=(i-)*block+,r[i]=i*block;
r[num]=n;
for(int i=;i<=n;i++)
belong[i]=(i-)/block+; for(int i=;i<=num;i++)
for(int j=l[i];j<=r[i];j++)
sum[i]+=a[j];
}
void update(int x,int y,int val){
if(belong[x]==belong[y]){
for(int i=x;i<=y;i++) a[i]+=val;
sum[belong[x]]+=(y-x+)*val;
return;
}
for(int i=x;i<=r[belong[x]];i++)a[i]+=val;
for(int i=belong[x]+;i<belong[y];i++){
sum[i]+=block*val;
lazy[i]+=val;
}
for(int i=l[belong[y]];i<=y;i++)a[i]+=val;
sum[belong[x]]+=(r[belong[x]]-x+)*val;
sum[belong[y]]+=(y-l[belong[y]]+)*val;
}
ll ask(int x,int y){
ll res=;
if(belong[x]==belong[y]){
for(int i=x;i<=y;i++)res+=a[i]+lazy[belong[x]];
return res;
}
for(int i=x;i<=r[belong[x]];i++)res+=a[i]+lazy[belong[x]];
for(int i=belong[x]+;i<belong[y];i++) res+=sum[i];
for(int i=l[belong[y]];i<=y;i++)res+=a[i]+lazy[belong[y]];
return res;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build();
while(q--){
int id,x,y,z;
scanf("%d",&id);
if(id==){
scanf("%d%d%d",&x,&y,&z);
update(x,y,z);
}
else{
scanf("%d%d",&x,&y);
printf("%lld\n",ask(x,y));
}
}
return ;
}

线段树解法:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
const ll maxn=1e6+;
int n,q;
ll a[maxn],tree[maxn*],lazy[maxn*];
void pushup(int rt){tree[rt]=tree[rt<<]+tree[rt<<|];}
void pushdown(int l,int r,int rt){
if(lazy[rt]){
tree[rt<<]+=l*lazy[rt];
tree[rt<<|]+=r*lazy[rt];
lazy[rt<<]+=lazy[rt];
lazy[rt<<|]+=lazy[rt];
lazy[rt]=;
}
}
void build(int l,int r,int rt){
lazy[rt]=;
if(l==r){
tree[rt]=a[l];
return;
}
int mid=l+r>>;
build(lson);
build(rson);
pushup(rt);
}
void update(int L,int R,ll val,int l,int r,int rt){
if(l>=L&&r<=R){
tree[rt]+=val*(r-l+);
lazy[rt]+=val;
return;
}
int mid=l+r>>;
pushdown(mid-l+,r-mid,rt);
if(mid>=L) update(L,R,val,lson);
if(mid<R) update(L,R,val,rson);
pushup(rt);
}
ll ask(int L,int R,int l,int r,int rt){
ll res=;
if(l>=L&&r<=R) return tree[rt];
int mid=l+r>>;
pushdown(mid-l+,r-mid,rt);
if(mid>=L) res+=ask(L,R,lson);
if(mid<R) res+=ask(L,R,rson);
return res;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
build(,n,);
while(q--){
int id,x,y,z;
scanf("%d",&id);
if(id==){
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,,n,);
}
else{
scanf("%d%d",&x,&y);
printf("%lld\n",ask(x,y,,n,));
}
}
return ;
}

Libre OJ 130、131、132 (树状数组 单点修改、区间查询 -> 区间修改,单点查询 -> 区间修改,区间查询)的更多相关文章

  1. 树状数组优化DP 【模拟赛】删区间

    哇,难受得一匹. 看到题的一瞬间竟然只想到了\(n^3\)的区间\(DP\) 一.\(40pts\) 设\(f[i][j]\)代表删去\(i\)到\(j\)这一段区间的最小代价和. 然后直接写普通的区 ...

  2. hdoj-4417(做法二 树状数组离线解法,对所有的查询先保存进行排序后有序的查询) 好腻害!

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std;; ; str ...

  3. 4.12 省选模拟赛 LCA on tree 树链剖分 树状数组 分析答案变化量

    LINK:duoxiao OJ LCA on Tree 题目: 一道树链剖分+树状数组的神题. (直接nQ的暴力有50. 其实对于树随机的时候不难想到一个算法 对于x的修改 暴力修改到根. 对于儿子的 ...

  4. codeforces 341d (树状数组)

    problem Iahub and Xors 题目大意 一个n*n的矩阵,要求支持两种操作. 操作1:将一个子矩阵的所有值异或某个数. 操作2:询问某个子矩阵的所以值的异或和. 解题分析 由于异或的特 ...

  5. HYSBZ 4551 (树状数组) 采花

    题目:这里 题意: 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记, ...

  6. POJ 3067 Japan(经典树状数组)

    基础一维树状数组  题意:左边一排 1-n 的城市,右边一排 1-m 的城市,都从上到下依次对应.接着给你一些城市对,表示城市这两个城市相连,最后问你一共有多少个交叉,其中处于城市处的交叉不算并且每个 ...

  7. HDU 4746 莫比乌斯反演+离线查询+树状数组

    题目大意: 一个数字组成一堆素因子的乘积,如果一个数字的素因子个数(同样的素因子也要多次计数)小于等于P,那么就称这个数是P的幸运数 多次询问1<=x<=n,1<=y<=m,P ...

  8. [HDOJ4325]Flowers(树状数组 离散化)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4325 关于离散化的简介:http://blog.csdn.net/gokou_ruri/article ...

  9. 树状数组初步_ZERO

    原博客:树状数组 1 一维树状数组 1 什么是树状数组        树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+-+A[n]的时,间是log级 ...

随机推荐

  1. MySQL 性能调优之SQL

    原文:http://bbs.landingbj.com/t-0-245451-1.html 对于SQL的优化,我们主要提供调整执行计划.优化SQL的方法有:缩短访问的路径.尽早过滤数据.尽可能减少排序 ...

  2. 测试python最大递归层次

    转自:https://www.cnblogs.com/xiongdashuai/p/6243372.html python默认的最大递归层数: 运行环境:Windows 7,x64python环境:p ...

  3. 百度地图api在Html中显示,在jsp页面中不显示解决方法

    在jsp页面中显示如下 但是在html中正常显示. 原来的代码如下: <script type="text/javascript" src="http://api. ...

  4. Oracle创建'数据库'三步走

    --创建表空间 create tablespace waterboss datafile 'd:\waterboss.dbf' size 100m autoextend on next 10m; -- ...

  5. js对字符串的一些操作方法

    1.charCodeAt(index); 返回一个整数,代表下标位置上字符的Unicode的编码. 2.fromCharCode(code1,code2,code3,...); code1代表Unic ...

  6. Y7000联想拯救者gtx1050Ti安装cuda9.0

    安装cuda经常会遇到安装失败的问题,原因大部分都是由于驱动不一致引起的,接下来我们分两步讲解 1. 安装驱动失败 原因:nvidia官网提供的驱动都是通用的,针对特殊设备可能出现不兼容问题,方法很简 ...

  7. 使用cmd命令行窗口操作SqlServer

    本文主要介绍使用windows下的使用cmd命令行窗口操作Sqlserver, 首先我们可以运行 osql  ?/   ,这样就把所有可以通过CMD命令行操作sqlserver的命令显示出来 (有图有 ...

  8. Razor Pages with ASP.NET Core 2

    With ASP.NET Core 2 we get another way of building web applications. It’s one of those new things th ...

  9. Logging - MVC Using Log4net Save to File and Database

    第一步:创建Config文件夹和log4net.config 第二步:在log4net.confg黏贴以下配置 <?xml version="1.0" encoding=&q ...

  10. django CBV视图源码分析

    典型FBV视图例子 url路由系统 from django.conf.urls import url from django.contrib import admin from luffycity.v ...