树状数组小结

单点修改&区间查询

最普通的树状数组
实现用lowbitlowbitlowbit的优秀性质
原理很简单


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 500005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll a[MAXN],tr[MAXN];
ll n,m;

O3 inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
    while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
O3 inline ll lowbit(ll x)
{
    return x&(-x);
}
O3 inline void add(ll x,ll y)
{
    while (x<=n)tr[x]+=y,x+=lowbit(x);
}
O3 inline ll query(ll x)
{
    ll y=0;
    while (x)y+=tr[x],x-=lowbit(x);
    return y;
}
O3 inline ll ask(ll x,ll y)
{
    return query(y)-query(x-1);
}
O3 int main()
{
    //freopen("readin.txt","r",stdin);
    n=read(),m=read();
    fo(i,1,n)a[i]=read(),add(i,a[i]);
    while (m--)
    {
        ll z=read(),x=read(),y=read();
        if (z==1)add(x,y);
        else printf("%lld\n",ask(x,y));
    }
    return 0;
}

区间修改&单点查询

实现这个的思路比较巧妙
做一个差分,设b[i]=a[i]−a[i−1]b[i]=a[i]-a[i-1]b[i]=a[i]a[i1],那么∑i=1nb[i]=a[i]\sum^n_{i=1}b[i]=a[i]i=1nb[i]=a[i]
那么对于一个区间的修改[x,y][x,y][x,y]区间加kkk,相当于xxx位加kkky+1y+1y+1位减kkk
容易知道这是对的,在xxx前没有影响,从xxx开始才有贡献
用树状数组维护bbb就好了


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 500005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;++i)
#define O3 __attribute__((optimize("-O3")))

ll a[MAXN],tr[MAXN];
ll n,m;

O3 inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
    while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
O3 inline ll lowbit(ll x)
{
    return x&(-x);
}
O3 inline void change(ll x,ll y)
{
    while (x<=n)tr[x]+=y,x+=lowbit(x);
}
O3 inline ll query(ll x)
{
    ll y=0;
    while (x)y+=tr[x],x-=lowbit(x);
    return y;
}
O3 int main()
{
    //freopen("readin.txt","r",stdin);
    n=read(),m=read();
    fo(i,1,n)a[i]=read(),change(i,a[i]-a[i-1]);
    while (m--)
    {
        ll z=read(),x,y;
        if (z==1)
        {
            x=read(),y=read(),z=read();
            change(x,z),change(y+1,-z);
        }
        else printf("%lld\n",query(read()));
    }
    return 0;
}

伪线段树树状数组

其实这个就是支持区间修改和区间查询的树状数组
对于一段区间查询[x,y][x,y][x,y],拆成[1,x−1][1,x-1][1,x1][1,y][1,y][1,y],单独考虑[1,x][1,x][1,x]的答案即可

  • 那么ans=∑i=1xa[i]=a[1]+a[2]+a[3]+...+a[x]ans=\sum^x_{i=1}a[i]=a[1]+a[2]+a[3]+...+a[x]ans=i=1xa[i]=a[1]+a[2]+a[3]+...+a[x]

  • =(b[1])+(b[1]+b[2])+(b[1]+b[2]+b[3])+...+(b[1]+b[2]+b[3]...+b[x])=(b[1])+(b[1]+b[2])+(b[1]+b[2]+b[3])+...+(b[1]+b[2]+b[3]...+b[x])=(b[1])+(b[1]+b[2])+(b[1]+b[2]+b[3])+...+(b[1]+b[2]+b[3]...+b[x])

  • =b[1]∗x+b[2]∗(x−1)+b[3]∗(x−2)+...+b[x]=b[1]*x+b[2]*(x-1)+b[3]*(x-2)+...+b[x]=b[1]x+b[2](x1)+b[3](x2)+...+b[x]

  • =x(b[1]+b[2]+b[3]+...+b[x])−(b[1]∗0+b[2]∗1+b[3]∗2+...+b[x]∗(x−1))=x(b[1]+b[2]+b[3]+...+b[x])-(b[1]*0+b[2]*1+b[3]*2+...+b[x]*(x-1))=x(b[1]+b[2]+b[3]+...+b[x])(b[1]0+b[2]1+b[3]2+...+b[x](x1))

再设一个ccc数组,使c[i]=b[i]∗(i−1)c[i]=b[i]*(i-1)c[i]=b[i](i1)
那么[1,x][1,x][1,x]的和即为x∗query(b,x)−query(c,x)x*query(b,x)-query(c,x)xquery(b,x)query(c,x)


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 500005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;++i)
#define O3 __attribute__((optimize("-O3")))

ll a[MAXN],tr[MAXN],tr1[MAXN];
ll n,m;

O3 inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
    while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
O3 inline ll lowbit(ll x)
{
    return x&(-x);
}
O3 inline void change(ll *tr,ll x,ll y)
{
    while (x<=n)tr[x]+=y,x+=lowbit(x);
}
O3 inline ll query(ll *tr,ll x)
{
    ll y=0;
    while (x)y+=tr[x],x-=lowbit(x);
    return y;
}
O3 int main()
{
    //freopen("readin.txt","r",stdin);
    n=read(),m=read();
    fo(i,1,n)a[i]=read(),change(tr,i,a[i]-a[i-1]),change(tr1,i,(a[i]-a[i-1])*(i-1));
    while (m--)
    {
        ll z=read(),x,y;
        if (z==1)
        {
            x=read(),y=read(),z=read();
            change(tr,x,z),change(tr,y+1,-z);
            change(tr1,x,z*(x-1)),change(tr1,y+1,-z*y);
        }
        else x=read(),y=read(),
        printf("%lld\n",(y*query(tr,y)-(x-1)*query(tr,x-1))-(query(tr1,y)-query(tr1,x-1)));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值