数据结构 树状数组

Posted cutele

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 树状数组相关的知识,希望对你有一定的参考价值。

问题 A: 数星星 Stars


时间限制: 1 Sec  内存限制: 128 MB
[命题人:admin]

题目描述

技术图片

输入

第一行一个整数 N,表示星星的数目;
接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y 坐标增序给出, y坐标相同的按 x 坐标增序给出。

输出

N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N-1 级的星星的数目。

样例输入 Copy

5
1 1
5 1
7 1
3 3
5 5

样例输出 Copy

1
2
1
1
0

提示

对于全部数据,1<=N<=1.5*104, 0<=x,y<=3.2*104。
 
技术图片
#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;
}
View Code

问题 B: 校门外的树


时间限制: 1 Sec  内存限制: 128 MB
[命题人:admin]

题目描述

校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作:

  • K=1,读入 l,r  表示在 l 到 r 之间种上一种树,每次操作种的树的种类都不同;
  • K=2,读入 l,r 表示询问 l 到 r 之间有多少种树。

注意:每个位置都可以重复种树。

输入

第一行 n,m  表示道路总长为 n,共有 m 个操作;
接下来 m 行为 m 个操作。

输出

对于每个 K=2 输出一个答案。

样例输入 Copy

5 4
1 1 3
2 2 5
1 2 4
2 3 5

样例输出 Copy

1
2

提示

对于 20% 的数据,1<=n,m<=100;
对于  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;
}
View Code

问题 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 名学生下车。

学生总人数不会超过 。

输出

对于每个 A ,输出一行,一个整数,表示年级主任的问题的答案。

样例输入 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;
}
View Code

问题 D: 简单题


时间限制: 1 Sec  内存限制: 512 MB
[命题人:admin]

题目描述

技术图片

输入

第一行包含两个整数 n,m,表示数组的长度和指令的条数;
以下  m行,每行的第一个数t  表示操作的种类:

  • 若 t=1,则接下来有两个数 L,R,表示区间 [L,R] 的每个数均反转;
  • 若 t=2,则接下来只有一个数 i,表示询问的下标。

输出

每个操作 2  输出一行(非0  即 1),表示每次操作2  的回答。

样例输入 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

提示

对于 50% 的数据,1<=n<=103,1<=m<=104;
对于 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;
}
 
View Code

问题 E: 单点修改,区间查询


时间限制: 3 Sec  内存限制: 128 MB
[命题人:admin]

题目描述

技术图片

输入

技术图片

输出

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

样例输入 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;
}
 
View Code

问题 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。

输出

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

样例输入 Copy

3 2
1 2 3
1 1 3 0
2 2

样例输出 Copy

2

提示

对于所有数据,1<=n,q<=106 , |a[i]|<=106, 1<=l<=r<=n, |x|<=106  。
 
技术图片
#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;
}
View Code

问题 G: 区间修改,区间查询


时间限制: 2 Sec  内存限制: 256 MB
[命题人:admin]

题目描述

技术图片

输入

技术图片

输出

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

样例输入 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

提示

对于所有数据,1<=n,q<=106,|a[i]|<=106 ,1<=l<=r<=n,|x[i]|<=106  。
 
技术图片
#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;
}
View Code

问题 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) 的子矩阵内所有数的和。

输入

输入的第一行有两个正整数 n,m ;
接下来若干行,每行一个操作,直到文件结束。

输出

对于每个 2 操作,输出一个整数,表示对于这个操作的回答。

样例输入 Copy

2 2
1 1 1 3
1 2 2 4
2 1 1 2 2

样例输出 Copy

7

提示

对于 10% 的数据,n=1;
对于另 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;
}
View Code

问题 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  分别表示矩阵的行数与列数。

接下来若干行直到文件结束,均代表你需要进行的操作。

输出

对于每个 2 操作,输出一行代表查询的结果。

样例输入 Copy

4 4
1 1 1 3 3 2
1 2 2 4 4 1
2 2 2 3 3

样例输出 Copy

12

提示

对于 10% 的数据,1<=n,m<=16,操作不超过 200 个;
对于 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;
}
View Code

 

以上是关于数据结构 树状数组的主要内容,如果未能解决你的问题,请参考以下文章

树状数组

树状数组

回顾树状数组

HDU 1556 数据结构-树状数组-改段求点

树状数组从入门到弃疗

数据结构-树状数组