ACM入门之【树状数组】

树状数组和线段树具有相似的功能,但他俩毕竟还有一些区别:树状数组能有的操作,线段树一定有;线段树有的操作,
树状数组不一定有。但是树状数组的代码要比线段树短,思维更清晰,速度也更快,在解决一些单点修改的问题时,树状数组是不二之选。

在这里插入图片描述
树状数组 数据结构详解与模板
它的各项操作的时间复杂度均为:O(logn)

常见的模型:

  • 单点修改,区间求和
  • 区间修改,单点求值
  • 区间修改,区间求值
  • 二维树状数组,单点修改,矩阵求值。
  • 二维树状数组, 区间修改 单点查询。
  • 二维树状数组, 区间修改,区间查询。

单点修改,区间求和模板

const int N=1e6+10;
typedef long long int LL;
LL tr[N],n;
int lowbit(int x){
    
    return x&(-x);}
void add(int u,int x)//u位置加x
{
    
    
	for(int i=u;i<=n;i+=lowbit(i)) tr[i]+=x;
}
LL query(int u)//求[1,u]的和
{
    
    
	LL sum=0;
	for(int i=u;i;i-=lowbit(i)) sum+=tr[i];
	return sum;
}

区间修改,单点求值模板:

//基于差分的思想
const int N=1e6+10;
typedef long long int LL;
LL tr[N],a[N],n,m;
int lowbit(int x){
    
    return x&(-x);}
void add(int u,int x)//u位置加x
{
    
    
	for(int i=u;i<=n;i+=lowbit(i)) tr[i]+=x;
}
LL query(int u)//求点u的值
{
    
    
	LL sum=0;
	for(int i=u;i;i-=lowbit(i)) sum+=tr[i];
	return sum;
}
void init()//初始化差分数列
{
    
    
    for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);
}
void solve(int l,int r,int x)//[l,r]加x
{
    
    
    add(l,x),add(r+1,-x);
}

区间修改,区间求值模板:

const int N=1e6+10;
typedef long long int LL;
LL tr1[N],tr2[N],a[N],n,m;
LL lowbit(LL x){
    
    return x&(-x);}
void add(LL tr[],LL u,LL c)
{
    
    
    for(int i=u;i<=n;i+=lowbit(i)) tr[i]+=c;
}
LL query(LL tr[],LL u)
{
    
    
    LL sum=0;
    for(int i=u;i;i-=lowbit(i)) sum+=tr[i];
    return sum;
}
LL sum(LL u)
{
    
    
    return 1ll*(u+1)*query(tr1,u)-query(tr2,u);
}
void init()//初始化
{
    
    
    for(int i=1;i<=n;i++)
    {
    
    
        add(tr1,i,a[i]-a[i-1]);
        add(tr2,i,i*(a[i]-a[i-1]));
    }
}
void add_lr(LL l,LL r,LL c)//[l,r]区间内加c
{
    
    
    add(tr1,l,c),add(tr1,r+1,-c);
    add(tr2,l,l*c),add(tr2,r+1,-(r+1)*c);
}
LL query_lr(LL l,LL r)//查询区间[l,r]
{
    
    
    return sum(r)-sum(l-1);
}

二维树状数组,单点修改,矩阵求值模板:

const int N=5050;
typedef long long int LL;
LL tr[N][N],n,m;
int lowbit(int x){
    
    return x&(-x);}
void add(int x,int y,int v)//[x,y]加v
{
    
    
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            tr[i][j]+=v;
}
LL query(int x,int y)
{
    
    
    LL sum=0;  
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j)) sum+=tr[i][j];
    return sum;
}
LL query_ans(int x,int y,int xx,int yy)
//找[x,y][xx,yy]为左上角和右下角的矩阵的和
{
    
    
    return query(xx,yy)-query(xx,y-1)-query(x-1,yy)+query(x-1,y-1);
}

二维树状数组, 区间修改 单点查询模板:

const int N=5050;
typedef long long int LL;
LL tr[N][N],n,m;
int lowbit(int x){
    
    return x&(-x);}
void add(int x,int y,int v)//[x,y]加v
{
    
    
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            tr[i][j]+=v;
}
LL query(int x,int y)//单点查询
{
    
    
    LL sum=0;  
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j)) sum+=tr[i][j];
    return sum;
}
void add1(int x,int y,int xx,int yy,int k)//区间加
{
    
    
    add(x,y,k),add(xx+1,y,-k);
    add(x,yy+1,-k),add(xx+1,yy+1,k);
}

二维树状数组, 区间修改,区间查询模板:

typedef long long LL;
const int N = 5000 + 10;
LL t1[N][N],t2[N][N],t3[N][N],t4[N][N];
int n,m;
int lowbit(int x) {
    
    return x & - x;}
void add(int a, int b, int k) 
{
    
    
    for (int i = a; i <= n; i += lowbit(i)) 
    {
    
    
        for (int j = b; j <= m; j += lowbit(j)) 
        {
    
    
            t1[i][j] += k;
            t2[i][j] += k * a;
            t3[i][j] += k * b;
            t4[i][j] += k * a * b;
        }
    }
}
LL sum(int a, int b) 
{
    
    
    LL ans=0;
    for (int i=a;i;i-=lowbit(i))
        for (int j=b;j;j-=lowbit(j))
            ans+=t1[i][j]*((a+1)*(b+1))-t2[i][j]*(b+1)-t3[i][j]*(a+1)+t4[i][j];
    return ans;
}
void add1(int a,int b,int c,int d,int k)
//[a,b][c,d]为左上角和右下角的矩阵所有值加k
{
    
    
    add(a, b, k);
    add(c + 1, d + 1, k);
    add(a, d + 1, -k);
    add(c + 1, b, -k);
}
LL query_ans(int a,int b,int c,int d)
//求区间[a,b],[c,d]为左上角和右下角的和
{
    
    
    return sum(a-1,b-1)+sum(c,d)-sum(a-1,d)-sum(c,b-1);
}

猜你喜欢

转载自blog.csdn.net/qq_46527915/article/details/123671861