zoukankan      html  css  js  c++  java
  • 树状数组求区间和模板 区间可修改 参考题目:牛客小白月赛 I 区间

    从前有个东西叫树状数组,它可以轻易实现一些简单的序列操作,比如单点修改,区间求和;区间修改,单点求值等.

    但是我们经常需要更高级的操作,比如区间修改区间查询.这时候树状数组就不起作用了,只能选择写一个2000GB的线段树交上去然后被卡常—–或者另一个选择是写ZKW线段树,会好一些.

    再但是…谁告诉你树状数组不能区间修改区间求和?告诉你,树状数组不仅能实现,而且代码依旧那么短小精悍.

    今天我们就来研究研究,如何实现这个更划算的数据结构.
    我们已经学会了树状数组的基本操作:单点修改区间查询,或区间修改单点查询(不会的话先去自学吧…这篇文章不适合你…).思考,区间修改单点求值是怎么做到的?只需要维护一个新数组c[i]=a[i]-a[i-1],也就是c[]是a[]的差分数组,修改区间[l,r]+v只需

    add(l,v);add(r+1,-v) //从l加到了n,r以后的多加了,所以要再进行次r+1到n加-v的操作

    即可.求某个值的时候,只需要把差分数组的前缀和求出来,就是要求的了.
    领悟了这个操作以后我们发现,化区间为单点的思想精髓就在于差分二字.利用差分思想,区间修改解决了,接下来就是区间求和公式的推导过程:
    sum(1,n)
    =a[1]+a[2]+a[3]+…+a[n-1]+a[n]
    =c[1]+(c[1]+c[2])+…+(c[1]+c[2]+…+c[n])
    =n*(c[1]+c[2]+…+c[n])-(0*c[1]+1*c[2]+2*c[3]+…+(n-1)*c[n]).
    发现什么了?
    我们开第二个树状数组c2,令c2[i]=c[i]*(i-1),那么…

    区间修改[l,r]+=v:
    add(c[l],v),add(c[r+1],-v);
    add(c2[l],(l-1)*v),add(c2[r+1],-r*v);

    求前缀和sum(1,n):
    sum(1,n)=n*query_c(n)-query_c2(n).

    求区间和sum(l,r):
    sum(l,r)=sum(r)-sum(l-1).

    至此,树状数组已经轻松实现了区间修改区间求和!

    例题:luogu 3372线段树模板 这题用线段树写500+ms,拿裸的树状数组311ms就切掉了,代码也养眼得多.至于zkw的效率如何我不太清楚.

    顺便:其实一开始建树的时候没必要把原来数组的元素一个个扔进树里,直接维护一个前缀和,然后计算的时候加上这个前缀和就好了.省去了nlogn的建树操作,会快很多.此处为了增强代码可读性,没有加这一句.

    参考博客:

    https://ahackh.ac.cn/2017/06/25/%E8%89%AF%E5%BF%83%E8%AF%A6%E8%A7%A3%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84%E3%81%AE%E5%8C%BA%E9%97%B4%E4%BF%AE%E6%94%B9%E6%B1%82%E5%92%8C%E6%9C%89%E8%BF%99%E7%A7%8D%E6%93%8D%E4%BD%9C/

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define N 102333
    using namespace std;
    typedef long long ll;
    int n,m;
    ll a[N],c1[N],c2[N];
    inline int lowbit(int x){return x&(-x);}
    void add(ll *r,int pos, ll v)
    {
        for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;
    }
    ll getsum(ll *r,int pos)
    {
        ll re=0;
        for(;pos>0;pos-=lowbit(pos))re+=r[pos];
        return re;
    }
    ll sigma(int r)
    {
        ll sum1=r*getsum(c1,r),sum2=getsum(c2,r);
        return sum1-sum2;
    }
    ll query(int x,int y)
    {
        return sigma(y)-sigma(x-1);
    }
    int flag,x,y;ll k;
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            add(c1,i,a[i]-a[i-1]);
            add(c2,i,(i-1)*(a[i]-a[i-1]));
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&flag);
            if(flag==1)
            {
                scanf("%d%d%lld",&x,&y,&k);
                add(c1,x,k);add(c1,y+1,-k);
                add(c2,x,(x-1)*k);add(c2,y+1,y*(-k));
            }
            else
            {
                scanf("%d%d",&x,&y);
                printf("%lld
    ",query(x,y));
            }
        }
        return 0;
    }
    

      

    类似题目: 区间

    链接:https://www.nowcoder.com/acm/contest/135/I
    来源:牛客网

    题目描述

        Apojacsleam喜欢数组。

        他现在有一个n个元素的数组a,而他要对a[L]-a[R]进行M次操作:

            操作一:将a[L]-a[R]内的元素都加上P

            操作二:将a[L]-a[R]内的元素都减去P

        最后询问a[l]-a[r]内的元素之和?
        请认真看题干及输入描述。

    输入描述:

    输入共M+3行:

    第一行两个数,n,M,意义如“题目描述”

    第二行n个数,描述数组。

    第3-M+2行,共M行,每行四个数,q,L,R,P,若q为1则表示执行操作2,否则为执行操作1

    第4行,两个正整数l,r

    输出描述:

    一个正整数,为a[l]-a[r]内的元素之和
    示例1

    输入

    复制
    10 5
    1 2 3 4 5 6 7 8 9 10
    1 1 5 5
    1 2 3 6
    0 2 5 5 
    0 2 5 8
    1 4 9 6
    2 7

    输出

    复制
    23

    说明

     
    直接裸模板就行
    //树状数组(升级版)
    #include <cstdio>
    #define lowbit(x) (x&-x)
    #define ll long long
    #define maxn 1000010
    using namespace std;
    ll n, q, c1[maxn], c2[maxn], num[maxn];
    void add(ll *r, ll pos, ll v)
    {for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;}
    ll sigma(ll *r, ll pos)
    {
    	ll ans;
    	for(ans=0;pos;pos-=lowbit(pos))ans+=r[pos];
    	return ans;
    }
    int main()
    {
    	ll i, j, type, a, b, v, sum1, sum2;
    	scanf("%lld",&n);
    	scanf("%lld",&q);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",num+i);
    		add(c1,i,num[i]-num[i-1]);
    		add(c2,i,(i-1)*(num[i]-num[i-1]));
    	}
    	while(q--)
    	{
    		scanf("%lld",&type);
    		if(type!=1)
    		{
    			scanf("%lld%lld%lld",&a,&b,&v);
    			add(c1,a,v);add(c1,b+1,-v);
    			add(c2,a,v*(a-1));add(c2,b+1,-v*b);
    		} else {
    		    scanf("%lld%lld%lld",&a,&b,&v);
    			add(c1,a,-v);add(c1,b+1,v);
    			add(c2,a,-v*(a-1));add(c2,b+1,v*b);
    		}
    	}
    	scanf("%lld%lld",&a,&b);
        sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);
        sum2=b*sigma(c1,b)-sigma(c2,b);
        printf("%lld
    ",sum2-sum1);
    	return 0;
    }
    

      

    彼时当年少,莫负好时光。
  • 相关阅读:
    在C#用HttpWebRequest中发送GET/HTTP/HTTPS请求
    微信公众平台开发(71)OAuth2.0网页授权
    IOS与安卓的远程调试
    解密和解压浏览器上加密的js文件
    appium 学习和环境搭建
    远程桌面工具 TeamViewer
    typescript 学习
    webstore 与 热编译的配置冲突
    CSS 温故而知新 断句失败
    自动化测试 python2.7 与 selenium 2 学习
  • 原文地址:https://www.cnblogs.com/l609929321/p/9366792.html
Copyright © 2011-2022 走看看