Libre OJ 130、131、132 (树状数组 单点修改、区间查询 -> 区间修改,单点查询 -> 区间修改,区间查询)
这三题均可以用树状数组、分块或线段树来做
#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=lra[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=lra[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=lra[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=lra[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 (树状数组 单点修改、区间查询 -> 区间修改,单点查询 -> 区间修改,区间查询)的更多相关文章
- 树状数组优化DP 【模拟赛】删区间
哇,难受得一匹. 看到题的一瞬间竟然只想到了\(n^3\)的区间\(DP\) 一.\(40pts\) 设\(f[i][j]\)代表删去\(i\)到\(j\)这一段区间的最小代价和. 然后直接写普通的区 ...
- hdoj-4417(做法二 树状数组离线解法,对所有的查询先保存进行排序后有序的查询) 好腻害!
#include<cstdio> #include<cstring> #include<algorithm> using namespace std;; ; str ...
- 4.12 省选模拟赛 LCA on tree 树链剖分 树状数组 分析答案变化量
LINK:duoxiao OJ LCA on Tree 题目: 一道树链剖分+树状数组的神题. (直接nQ的暴力有50. 其实对于树随机的时候不难想到一个算法 对于x的修改 暴力修改到根. 对于儿子的 ...
- codeforces 341d (树状数组)
problem Iahub and Xors 题目大意 一个n*n的矩阵,要求支持两种操作. 操作1:将一个子矩阵的所有值异或某个数. 操作2:询问某个子矩阵的所以值的异或和. 解题分析 由于异或的特 ...
- HYSBZ 4551 (树状数组) 采花
题目:这里 题意: 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记, ...
- POJ 3067 Japan(经典树状数组)
基础一维树状数组 题意:左边一排 1-n 的城市,右边一排 1-m 的城市,都从上到下依次对应.接着给你一些城市对,表示城市这两个城市相连,最后问你一共有多少个交叉,其中处于城市处的交叉不算并且每个 ...
- HDU 4746 莫比乌斯反演+离线查询+树状数组
题目大意: 一个数字组成一堆素因子的乘积,如果一个数字的素因子个数(同样的素因子也要多次计数)小于等于P,那么就称这个数是P的幸运数 多次询问1<=x<=n,1<=y<=m,P ...
- [HDOJ4325]Flowers(树状数组 离散化)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4325 关于离散化的简介:http://blog.csdn.net/gokou_ruri/article ...
- 树状数组初步_ZERO
原博客:树状数组 1 一维树状数组 1 什么是树状数组 树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+-+A[n]的时,间是log级 ...
随机推荐
- 三、如何设置npm镜像
一.临时使用 npm --registry https://registry.npm.taobao.org install express 二.永久使用 npm config set registry ...
- C#设计模式之6:抽象工厂模式
前面分析了简单工厂模式和工厂方法模式,接着来看一下抽象工厂模式,他与工厂方法模式有一些相似的地方,也有不同的地方. 先来看一个不用工厂方法模式实现的订购披萨的代码: 对象依赖的问题:当你直接实例化一个 ...
- webservice服务的提供及调用完整代码示例
服务提供方: applicationContext.xml applicationContext-webService.xml 服务调用方:
- Spring 配置详解
spring4配置文件详解 一.配置数据源 基本的加载properties配置文件 <context:property-placeholder location="classpath* ...
- 【转】Java基础——容器分类
Java容器可以说是增强程序员编程能力的基本工具,本系列将带您深入理解容器类. 容器的用途 如果对象的数量与生命周期都是固定的,自然我们也就不需要很复杂的数据结构. 我们可以通过创建引用来持有对象,如 ...
- CentOS7 网络NAT模式
问题:安装完毕ping命令不能用,然后改为桥接模式,ping可以用. 先了解桥接,NAT 的含义. 桥接:在bridged模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机,它可以访 ...
- python之路--基础数据类型的补充与深浅copy
一 . join的用法 lst =['吴彦祖','谢霆锋','刘德华'] s = '_'.join(lst) print(s) # 吴彦祖_谢霆锋_刘德华 # join() "*" ...
- sed 双引号 单引号的区别
a="abcd" b="abc" sed -i '/$a/ s/$/$b/' test.a 我想在test.a中匹配以”abcd“开头的行,然后在行尾加入”ab ...
- Calendar用法随笔
平时在处理时间问题的时候,一般会想到用java.util.Date类型,在使用倒时间的运算的时候,就不是很方便,找找到了java.util.Calendar类,中文意思是“日历”,以下就是自己对这个类 ...
- MobX基础 ----- 类的静态属性和装饰器
当我们使用MobX的时候,首先要声明一个store, 用来保存状态,它的最基本的语法 如下: class Todo { @observable title = ""; @obser ...