zoukankan      html  css  js  c++  java
  • 树状数组习题(一本通)

    1535:【例 1】数列操作

    模板题

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=101000;
    const int INF=0x3fffffff;
    typedef long long LL;
    #define lowbit(x)  ((x)&(-x))
    int tree[maxn];
    int n,m;
    void add(int x,int d){
    	while(x<=n){
    		tree[x]+=d;
    		x+=lowbit(x);
    	}
    }
    LL getsu(int x){
    	LL ans=0;
    	while(x>0){
    		ans+=tree[x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    int main(){
    	
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++) {
    		int x;
    		scanf("%d",&x);
    		add(i,x);
    	}
    	int k,a,b;
    	while(m--){
    		scanf("%d %d %d",&k,&a,&b);
    		if(k==0) printf("%lld
    ",getsu(b)-getsu(a-1));
    		else add(a,b);
    	}
    return 0;
    }
    

      

    1536:【例 2】数星星 Stars

    //二维的树状数组bushi
    //认真读题呀!!!!!看给出数据的特征,是按照纵坐标从小到大排序的,纵坐标相同的是横坐标从小到大给出的
    //也就是说,我们可以不管纵坐标,按照它给出的横坐标依次插入,并统计当前星星之前的横坐标小于它的星星个数。

    观察输入数据的特点

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    int n;
    int c[100005];
    int ans[100055];
    int maxn=32001;
    struct node{
    	int x,y;
    }a[100005];
    int lowbit(int x)
    {
    	return x&(-x);
    }
    void update(int x,int y)
    {
    	while(x<=maxn)
    	{
    		c[x]+=y;
    		x+=lowbit(x);
    	}
    }
    int sum(int x)
    {
    	int cnt=0;
    	while(x>0)
    	{
    		cnt+=c[x];
    		x-=lowbit(x);
    	}
    	return cnt;
    }
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&a[i].x,&a[i].y);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		int wzx=a[i].x+1;
    		int jd=sum(wzx);   //先计算个数,为什么不剪掉1,因为包括了正左、正下
    		update(wzx,1);     //再进行更新
    		ans[jd]++; 
    	}
    	for(int i=0;i<n;i++)
    	printf("%d
    ",ans[i]);
    	return 0;
    } 
    

      

    1537:【例 3】校门外的树

     注意,这里不是点更新了,而是区间更新,区间查找,而对于区间更新:维护两个数组,一个负责开始,一个负责结尾

    左右括号法。
    对于每次操作[a,b],将位于a的左括号个数加一,位于b的右括号个数加一。
    对于每次查询[a,b],定义X等于1到b的左括号个数,Y等于1到a−1的右括号个数,答案即为X−Y
    https://blog.csdn.net/zhang14369/article/details/81071990

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    const int maxn=5e4+10; 
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int n,m;
    int k;
    //其他的做法:
    //https://www.cnblogs.com/ECJTUACM-873284962/p/7060158.html 
    //这道题需要维护两个 开两个数组来存一个是开始的点的数量,一个是结束的 ,然后随便搞一下,最后输出就可以了
    int a[maxn],b[maxn];
    
    int getsum(int l,int r){
    	int ans=0;
    	for(int i=r;i;i-=lowbit(i)) ans+=a[i];
    	for(int i=l-1;i;i-=lowbit(i)) ans-=b[i];
    	return ans;
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	int l,r;
    	for(int i=0;i<m;i++){
    		scanf("%d %d %d",&k,&l,&r);
    		if(k==1) {
    				for(int i=l;i<=n+1;i+=lowbit(i)) a[i]++;
    				for(int i=r;i<=n+1;i+=lowbit(i)) b[i]++;
    			
    		}
    		else {
    			printf("%d
    ",getsum(l,r));
    		}
    	}
    return 0;
    }
    

      另一道区间求和、区间查询:利用差分的思想

    1548:【例 2】A Simple Problem with Integers

     讲解:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e6+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //这道题是模板题:区间求和、区间修改
    //可以用线段树、也可以用树状数组
    //感觉线段树简单一点,但是不好推
    //用树状数组讲解:维护两个前缀和
    //https://blog.csdn.net/gzcszzx/article/details/100539427
    /*
    维护两个前缀和,
    S1[i]=d[i],S2[i]=d[i]*i
    查询:位置Pos的前缀和就是(Pos+1)*S1中1到Pos的和 减去 S2中1到Pos的和,[L,R]=SS[R]-SS[L-1]
     
    修改:[L,R] 
    S1:S1[L]+Tag,S1[R+1]-Tag  
    S2:S2[L]+Tag*L ,S2[R+1]-Tag*(R+1)
    */
    LL n,m;
    LL a[maxn],d[maxn];  //a[i]为原数组  d[i]为差分数组
    LL c1[maxn],c2[maxn];  //两个前缀和
    #define lowbit(x) ((x)&(-x))
    void add(LL x,LL v){
        LL p=x;
        while(x<=n){
            c1[x]+=v;
            c2[x]+=p*v;
            x+=lowbit(x);
        }
    }
    LL getans(LL x){
        LL ans=0,p=x;
        while(x){
            ans+=(p+1)*c1[x]-c2[x];
            x-=lowbit(x);
        }
        return ans;
    }
    int main(){
        scanf("%lld %lld",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            d[i]=a[i]-a[i-1];
            add(i,d[i]);
        }
        while(m--){
            int p;
            scanf("%d",&p);
            if(p==1){
                LL l,r,c;
                scanf("%lld %lld %lld",&l,&r,&c);
                add(l,c);
                add(r+1,-c);
            }
            if(p==2){
                LL x,y;
                scanf("%lld %lld",&x,&y);
                printf("%lld
    ",getans(y)-getans(x-1));
            }
        }
    return 0;
    }
    

      

    1538:清点人数

    模板题

    数据时cin铁定超时,换用scanf问题就来了,用getchar()读回车不知为何会错,看到别人的博客,读字符是scanf(" %c",&op);这样的,前面多一个空格,也能解决换行符问题,记一下.

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    const int maxn=5e5+100;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int n,k;
    char a;
    int op[maxn];
    void add(int x,int k){
    	for(int i=x;i<=n;i+=lowbit(i))  op[i]+=k;
    }
    LL getans(int pos){
    	LL ans=0;
    	for(int i=pos;i;i-=lowbit(i)) ans+=op[i];
    	return ans;
    }
    int main(){
    	scanf("%d %d",&n,&k);
    	//getchar();
    	int m,p;
    	for(int i=0;i<k;i++){
    		scanf(" %c",&a);
    		//读数据时cin铁定超时,换用scanf问题就来了,用getchar()读回车不知为何会错,看到别人的博客,读字符是scanf(" %c",&op);这样的,前面多一个空格,也能解决换行符问题,记一下.
    		if(a=='A'){
    			scanf("%d",&m);
    			printf("%lld
    ",getans(m));
    		}
    		else if(a=='B'){
    			scanf("%d %d",&m,&p);
    			add(m,p);
    		}
    		else if(a=='C'){
    			scanf("%d %d",&m,&p);
    			add(m,-p);
    		}
    		//getchar();
    	}
    return 0;
    }
    

      

    1539:简单题

     这题也是,区间更新,点查询-

    表达的内容改变了:c[]记录的是这个位置的数改变了多少次,这道题也可以用线段树做

    但是如果两种操作不反转的话:差分的思路??

    树状数组维护差分数组
    差分数组修改区间
    只需要在区间左端点加上修改的值   add(l,1);
    在右端点之后减去修改的值就好了  add(r+1,-1);
    求某个位置上的值
    就是这个位置之前(包括这个位置)的和   getans(l)%2
    也符合树状数组里面的sum
    就不需要做差了
    最后按照%2来输出就好了
    因为翻转两次之后会回到原来的情况

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    const int maxn=1e5+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //需要让数组01反转
    //而且反转的是区间,
    int  c[maxn];
    //用C[i]表示对其区间表示范围内的元素修改了几次  !!!不要忘了这种啊 
    //故可以用类似于树状数组中点修改的操作
    int n,m;
    //呜呜呜呜我错了,这是区间修改和点查询,两种的操作要反过来 
    void add(int x,int k){
    	while(x<=n){
    		c[x]+=k;
    		x+=lowbit(x);
    	}
    }
    int getans(int x){
    	int ans=0;
    	while(x){
    		ans+=c[x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	int t,l,r;
    	while(m--){
    		scanf("%d",&t);
    		if(t==1){
    			scanf("%d %d",&l,&r);
    			add(l,1);
    			add(r+1,-1);  //如果不反过来的话为甚恶魔是这样呢 ,减少一次更新的情况 
    		}
    		else if(t==2){
    			scanf("%d",&l);
    			printf("%d
    ",getans(l)%2);
    		}
    	}
    return 0;
    }
    

      

    1540:打鼹鼠_二维树状数组

     二维树状数组,最后一个点过不了

    有两个注意的细节:

    1)再add的时候,数据范围是x<=maxn,tempy<=maxn

    2)在计算结果的时候,是需要这样减去的ask(c,d)-ask(a-1,d)-ask(c,b-1)+ask(a-1,b-1)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    const int maxn=5004; 
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int  mp[maxn][maxn];
    int n,m;
    //!!!!为什么最后一个点过不了!!!! 
    void add(int x,int y,int xx){
    	int y1;
    	while(x<=maxn){
    		y1=y;
    		while(y1<=maxn){
    			mp[x][y1]+=xx;
    			y1+=lowbit(y1);
    		}
    		x+=lowbit(x);
    	}
    }
    LL ask(int x,int y){
    	LL ans=0;
    	while(x){
    		int temp=y;
    		while(temp){
    			ans+=mp[x][temp];
    			temp-=lowbit(temp);
    		}
    		x-=lowbit(x);
    	}
    	return ans;
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	int op,xx,c,d,a,b;
    	while(scanf("%d",&op)!=EOF){
    		
    		if(op==1){
    			scanf("%d %d %d",&a,&b,&xx);
    			add(a,b,xx);
    		}
    		else if(op==2){
    			scanf("%d %d %d %d",&a,&b,&c,&d);
    			
    			printf("%lld
    ",ask(c,d)-ask(a-1,d)-ask(c,b-1)+ask(a-1,b-1));
    			//这样减的!!!!! 
    		}
    	}
    return 0;
    }
    

      

  • 相关阅读:
    定时器
    javascript之循环保存数值
    Web 前端之HTML和CSS
    [讲解]容斥原理
    [vijos1048]送给圣诞夜的贺卡<DFS剪枝>
    [vijos1145]小胖吃巧克力<概率dp>
    [noip2012]国王游戏<贪心+高精度>
    [codevs3118]高精度除法<高精度>
    [noip2016]组合数问题<dp+杨辉三角>
    [codevs2370]小机房的树<LCA>
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12732595.html
Copyright © 2011-2022 走看看