数据结构 树状数组
Posted cutele
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 树状数组相关的知识,希望对你有一定的参考价值。
问题 A: 数星星 Stars
时间限制: 1 Sec 内存限制: 128 MB
[命题人:admin]
题目描述
输入
接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y 坐标增序给出, y坐标相同的按 x 坐标增序给出。
输出
样例输入 Copy
5
1 1
5 1
7 1
3 3
5 5
样例输出 Copy
1
2
1
1
0
提示
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; int a[N],c[N];//原数组和树状数组 int n; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } inline int lowbit(int x){ return x&-x; } void add(int x){ for(int i=x;i<N;i+=lowbit(i)) c[i]++; } ll sum(int i){//求a[1]到a[n]的和 ll res=0; while(i>0){ res+=c[i]; i-=lowbit(i); } return res; } int main(){ n=read(); for(int i=0;i<n;i++){ int x,y; x=read();y=read(); x++; a[sum(x)]++; add(x); } for(int i=0;i<n;i++) printf("%d ",a[i]); return 0; }
问题 B: 校门外的树
时间限制: 1 Sec 内存限制: 128 MB
[命题人:admin]
题目描述
校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作:
- K=1,读入 l,r 表示在 l 到 r 之间种上一种树,每次操作种的树的种类都不同;
- K=2,读入 l,r 表示询问 l 到 r 之间有多少种树。
注意:每个位置都可以重复种树。
输入
接下来 m 行为 m 个操作。
输出
样例输入 Copy
5 4
1 1 3
2 2 5
1 2 4
2 3 5
样例输出 Copy
1
2
提示
对于 60%的数据,1<=n<=103, 1<=m<=5*104;
对于 100%的数据,1<=n,m<=5*104,保证l,r>0 。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; int a[N],b[N]; int n,m,k,l,r; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } inline int lowbit(int x){ return x&-x; } inline void update1(int x,int k){ while(x<N){ a[x]+=k; x+=lowbit(x); } } inline void update2(int x,int k){ while(x<N){ b[x]+=k; x+=lowbit(x); } } inline ll sum1(int x){ ll res=0; while(x){ res+=a[x]; x-=lowbit(x); } return res; } inline ll sum2(int x){ ll res=0; while(x){ res+=b[x]; x-=lowbit(x); } return res; } int main(){ cin>>n>>m; while(m--){ cin>>k>>l>>r; if(k==1) update1(l,1),update2(r,1); else cout<<sum1(r)-sum2(l-1)<<endl; } return 0; }
问题 C: 清点人数
时间限制: 23 Sec 内存限制: 128 MB
[命题人:admin]
题目描述
NK 中学组织同学们去五云山寨参加社会实践活动,按惯例要乘坐火车去。由于 NK 中学的学生很多,在火车开之前必须清点好人数。
初始时,火车上没有学生。当同学们开始上火车时,年级主任从第一节车厢出发走到最后一节车厢,每节车厢随时都有可能有同学上下。年级主任走到第 m 节车厢时,他想知道前 m 节车厢上一共有多少学生,但是他没有调头往回走的习惯。也就是说每次当他提问时, m总会比前一次大。
输入
第一行两个整数n,k ,表示火车共有 n 节车厢以及 k 个事件。
接下来有 k 行,按时间先后给出 k 个事件,每行开头都有一个字母 A,B 或 C。
- 如果字母为 A,接下来是一个数 m ,表示年级主任现在在第 m 节车厢;
- 如果字母为 B,接下来是两个数 m,p,表示在第 m 节车厢有 p 名学生上车;
- 如果字母为 C,接下来是两个数 m,p,表示在第 m节车厢有 p 名学生下车。
学生总人数不会超过 。
输出
样例输入 Copy
10 7
A 1
B 1 1
B 3 1
B 4 1
A 2
A 3
A 10
样例输出 Copy
0
1
2
3
提示
对于 30% 的数据,1<=n,k<=104,至少有 3000 个 A;
对于 100% 的数据,1<=n<=5*105, 1<=k<=105,至少有 3*104个 A。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; ll a[N], n,k; inline ll lowbit(ll x){ return x&-x; } inline void update(ll i,ll x){ while(i<N){ a[i]+=x; i+=lowbit(i); } } inline ll sum(ll x){ ll res=0; while(x){ res+=a[x]; x-=lowbit(x); } return res; } int main(){ cin>>n>>k; while(k--){ string op; cin>>op; if(op=="A"){ ll m; cin>>m; cout<<sum(m)<<endl; } else if(op=="B"){ ll m,p; cin>>m>>p; update(m,p); } else if(op=="C"){ ll m,p; cin>>m>>p; update(m,-p); } } return 0; }
问题 D: 简单题
时间限制: 1 Sec 内存限制: 512 MB
[命题人:admin]
题目描述
输入
第一行包含两个整数 n,m,表示数组的长度和指令的条数;
以下 m行,每行的第一个数t 表示操作的种类:
- 若 t=1,则接下来有两个数 L,R,表示区间 [L,R] 的每个数均反转;
- 若 t=2,则接下来只有一个数 i,表示询问的下标。
输出
样例输入 Copy
20 10
1 1 10
2 6
2 12
1 5 12
2 6
2 15
1 6 16
1 11 17
2 12
2 6
样例输出 Copy
1
0
0
0
1
1
提示
对于 100%的数据,1<=n<=105,1<=m<=5*105,保证 L<=R。
#pragma GCC optimize(s) #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; ll a[N], n,k; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } inline ll lowbit(ll x){ return x&-x; } inline void update(ll i){ while(i<N){ a[i]^=1; i+=lowbit(i); } } inline ll sum(ll x){ ll res=0; while(x){ res^=a[x]; x-=lowbit(x); } return res; } //区间修改 单点查询 ^ int main(){ n=read();k=read(); while(k--){ int t; t=read();; if(t==1){ int l,r; l=read();r=read(); update(l);update(r+1); } else{ int i; i=read(); printf("%lld ",sum(i)); } } return 0; }
问题 E: 单点修改,区间查询
时间限制: 3 Sec 内存限制: 128 MB
[命题人:admin]
题目描述
输入
输出
样例输入 Copy
3 2
1 2 3
1 2 0
2 1 3
样例输出 Copy
6
提示
#pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } ll a[N],c[N],n; inline int lowbit(int x){ return x&-x; } inline void add(int i,int x){ while(i<=n){ c[i]+=x; i+=lowbit(i); } return ; } inline ll sum(int i){ ll res=0; while(i){ res+=c[i]; i-=lowbit(i); } return res; } int main(){ int q; n=read();q=read(); for(int i=1;i<=n;i++){ int x; x=read(); add(i,x); } while(q--){ int l,r,w; w=read();l=read();r=read(); if(w==1) add(l,r); else { ll res=sum(r)-sum(l-1); printf("%lld ",res); } } return 0; }
问题 F: 区间修改,单点查询
时间限制: 3 Sec 内存限制: 128 MB
[命题人:admin]
题目描述
给定数列a[1], a[2], ... , a[n] ,你需要依次进行 q 个操作,操作有两类:
- 1 l r x:给定 l,r,x,对于所有 i in [l,r],将 a[i] 加上 x(换言之,将 a[l], a[l+1],...,a[r] 分别加上 x);
- 2 i:给定 i ,求 a[i] 的值。
输入
第一行包含 2 个正整数 n,q,表示数列长度和询问个数。保证1<=n,q<=106 。
第二行 n 个整数 a[1], a[2],..., a[n],表示初始数列。保证|a[i]|<=106 。
接下来 q 行,每行一个操作,为以下两种之一:
- 1 l r x:对于所有 i in [l,r],将 a[i] 加上 x;
- 2 i:给定 i,求 a[i] 的值。
保证 1<=l<=r<=106, |x|<=106。
输出
样例输入 Copy
3 2
1 2 3
1 1 3 0
2 2
样例输出 Copy
2
提示
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; ll a[N],c[N];//原数组和树状数组 ll n; inline ll read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } inline ll lowbit(ll x){ return x&-x; } void add(ll i,ll k){ while(i<=n){ c[i]+=k; i+=lowbit(i); } } ll sum(ll i){//求a[1]到a[n]的和 ll res=0; while(i>0){ res+=c[i]; i-=lowbit(i); } return res; } int main(){ ll q,x; n=read();q=read(); for(ll i=1;i<=n;i++){ a[i]=read(); add(i,a[i]-a[i-1]); } while(q--){ ll a,b,c,d; a=read(); if(a==1){ b=read();c=read();d=read(); add(b,d); add(c+1,-d); } else{ b=read(); printf("%lld ",sum(b)); } } return 0; }
问题 G: 区间修改,区间查询
时间限制: 2 Sec 内存限制: 256 MB
[命题人:admin]
题目描述
输入
输出
样例输入 Copy
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
样例输出 Copy
15
34
32
33
50
提示
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+7; ll a[N],sum1[N],sum2[N]; ll n,m; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } inline ll lowbit(ll x){ return x&-x; } inline void update(ll i,ll k){ ll x=i; while(i<=n){ sum1[i]+=k; sum2[i]+=k*(x-1); i+=lowbit(i); } } inline ll sum(int i){ ll res=0,x=i; while(i>0){ res+=x*sum1[i]-sum2[i]; i-=lowbit(i); } return res; } int main(){ n=read();m=read(); for(ll i=1;i<=n;i++){ cin>>a[i]; update(i,a[i]-a[i-1]); } while(m--){ ll k,l,r,x; k=read(); if(k==1){ l=read();r=read();x=read(); update(l,x); update(r+1,-x); } else{ l=read();r=read(); ll res=sum(r)-sum(l-1); printf("%lld ",res); } } return 0; }
问题 H: 单点修改,区间查询
时间限制: 5 Sec 内存限制: 512 MB
[命题人:admin]
题目描述
给出一个 n*m 的零矩阵 A,你需要完成如下操作:
- 1 x y k:表示元素 Ax,y 自增 k;
- 2 a b c d:表示询问左上角为 (a,b),右下角为 (c,d) 的子矩阵内所有数的和。
输入
接下来若干行,每行一个操作,直到文件结束。
输出
样例输入 Copy
2 2
1 1 1 3
1 2 2 4
2 1 1 2 2
样例输出 Copy
7
提示
对于另 10%的数据,m=1;
对于全部数据,1<=n,m<=212,1<=x,a,c<=n,1<=y,b,d<=m,|k|<=105,保证操作数目不超过 3*105,且询问的子矩阵存在。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=5500; ll a[N][N],n,m; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } ll lowbit(ll x){ return x&-x; } void update(ll x,ll y,ll v){ for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) a[i][j]+=v; } ll sum(ll x,ll y){ ll res=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) res+=a[i][j]; return res; } ll query(ll x1,ll y1,ll x2,ll y2){ return sum(x1,y1)-sum(x1,y2-1)-sum(x2-1,y1)+sum(x2-1,y2-1); } int main(){ while(~scanf("%d%d",&n,&m)){ memset(a,0,sizeof a); int op; while(~scanf("%d",&op)){ if(op==1){ ll x,y,k; x=read();y=read();k=read(); //cin>>x>>y>>k; update(x,y,k); } else{ ll a,b,c,d; c=read();d=read();a=read();b=read(); //cin>>a>>b>>c>>d; printf("%lld ",query(a,b,c,d)); } } } return 0; }
问题 I: 区间修改,区间查询
时间限制: 15 Sec 内存限制: 256 MB
[命题人:admin]
题目描述
给定一个大小为 N*M 的零矩阵,直到输入文件结束,你需要进行若干个操作,操作有两类:
-
1 a b c d x,表示将左上角为 (a,b) ,右下角为 (c,d) 的子矩阵全部加上 x;
-
2 a b c d,表示询问左上角为 (a,b),右下角为 (c,d) 为顶点的子矩阵的所有数字之和。
输入
第一行两个正整数 n,m ,其中n,m 分别表示矩阵的行数与列数。
接下来若干行直到文件结束,均代表你需要进行的操作。
输出
样例输入 Copy
4 4
1 1 1 3 3 2
1 2 2 4 4 1
2 2 2 3 3
样例输出 Copy
12
提示
对于 60%的数据,1<=n,m<=512;
对于 100% 的数据,1<=n,m<=2048,|x|<=500,操作不超过 2*105个,保证运算过程中及最终结果均不超过 64 位带符号整数类型的表示范围,并且修改与查询的子矩阵存在。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2100; ll t1[N][N],t2[N][N],t3[N][N],t4[N][N]; ll n,m; ll lowbit(ll x){ return x&(-x); } void add(ll x,ll y,ll v){ for(ll i=x;i<=n;i+=lowbit(i)) for(ll j=y;j<=m;j+=lowbit(j)){ t1[i][j]+=v; t2[i][j]+=v*x; t3[i][j]+=v*y; t4[i][j]+=v*x*y; } } void update(ll x1,ll y1,ll x2,ll y2,ll v){ add(x1,y1,v); add(x1,y2+1,-v); add(x2+1,y1,-v); add(x2+1,y2+1,v); } ll sum(ll x,ll y){ ll res=0; for(ll i=x;i>0;i-=lowbit(i)) for(ll j=y;j>0;j-=lowbit(j)) res+=(x+1)*(y+1)*t1[i][j]-(y+1)*t2[i][j]-(x+1)*t3[i][j]+t4[i][j]; return res; } ll query(ll x1,ll y1,ll x2,ll y2){ return sum(x2,y2)+sum(x1-1,y1-1)-sum(x2,y1-1)-sum(x1-1,y2); } int main(){ scanf("%lld%lld", &n, &m); int op; while(~scanf("%d",&op)){ if(op==1){ ll a,b,c,d,x; scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &x); // a=read();b==read();c=read();d=read();x=read(); update(a,b,c,d,x); } else{ ll a,b,c,d; scanf("%lld%lld%lld%lld", &a, &b, &c, &d); //a=read();b==read();c=read();d=read(); printf("%lld ",query(a,b,c,d)); } } return 0; }
以上是关于数据结构 树状数组的主要内容,如果未能解决你的问题,请参考以下文章