树状数组专题

基础和原理可以看看我写的总结:http://blog.csdn.net/WilliamSun0122/article/details/70679766

一维 单点更新 区间查询

这个用法是树状数组最基础的用法,我的总结里面就是以这个用法为例讲的。直接贴模板,详细见我树状数组总结的博客。

int lowbit(int x)
{
    return x&(-x);
}

int update(int x,int value)
{
    while(x<=maxn)
    {
        sz[x] += value;
        x += lowbit(x);
    }
}

int query(int x)
{
    int ans = 0;
    while(x>0)
    {
        ans += sz[x];
        x -= lowbit(x);
    }
    return ans;
}

一维 区间更新 单点查询

该种用法详情见我以hdu1556为模板写的博客:http://blog.csdn.net/WilliamSun0122/article/details/71511879

简单说一下:
这种用法主要是通过维护差分数组实现的。
设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[n]= ni=1c[i] ,如果想要修改a[i]到a[j] (比如+v),只需令c[i]+=v,c[j+1]-=v即可,到这区间更新就实现了。

单点查询就直接查询查询即可。因为你维护的是差分数组c[],查询时query(n)是查询c[1]+…+c[n],而其就等于 a[n],即实现单点查询。

比如对a[]的区间[3,5]都+1
核心代码:

//lowbit(),update(),query()与一维单点更新,区间查询一样
update(3,1);
update(6.-1);

会了之后可以去把hdu1556模板题秒了。

一维 区间更新 区间查询

该种用法详情见我以poj3468为模板写的博客:http://blog.csdn.net/williamsun0122/article/details/71571143

简单说一下:
这种用法是维护差分数组c[n]的同时维护另一数组c2[n] = n*c[n]。区间更新与一维 区间更新 单点查询一样。区间查询:
a[1] + a[2] + … + a[n]
= c[1] + (c[1]+c[2]) + … + (c[1]+c[2]+…+c[n])
= n*c[1] + (n-1)*c[2] + … + c[n]
= (n+1)*(c[1]+…+c[n]) - (1*c[1] + 2*c[2] + … + n*c[n])

模板:

//lowbit()与之前一样
void update(int x,int value)
{
    int i=x;
    while(x<=n)
    {
        c1[x] += value;
        c2[x] += i*value;
        x += lowbit(x);
    }
}

int query(int x)
{
    int tmp1=0,tmp2=0,i=x;
    while(x>0)
    {
        tmp1 += c1[x];
        tmp2 += c2[x];
        x -= lowbit(x);
    }
    return (i+1)*tmp1-tmp2; 
}

多维与一维没有本质差别,可以通过一维类推得到。以二维为例

二维 单点更新 区间查询

详细见我以hdu2642为模板写的博客:http://blog.csdn.net/WilliamSun0122/article/details/71642358
想知道二维树状数组具体构成就看看上面的博客。
其实没什么说的,直接一维类推即可。查询就是查询一个矩形区域。

// lowbit()是一样的
void update(int x,int y,int value)
{
    for(int i=x;i<=maxn;i+=lowbit(i))
        for(int j=y;j<=maxn;j+=lowbit(j))
            sz[i][j] += value;
}

int query(int x,int y)
{
    int ans = 0;
    for(int i=x;i>0;i-=lowbit(i))
        for(int j=y;j>0;j-=lowbit(j))
            ans += sz[i][j];
    return ans;
}

//比如我们要查询x1,y1,x2,y2这个矩形区间的和(x1
//query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1)

二维 区间更新 单点查询

详细见我以poj2155为模板写的博客:http://blog.csdn.net/WilliamSun0122/article/details/71763373
多维以上有关区间都用了容斥原理。
就像这个图,我们想对蓝色区间内修改一次,那么如果我们改变第一个蓝色的点会造成紫色区间内的修改,所以我们必须再通过修改黄、绿、红来抵消这些多余的影响。
这里写图片描述

//区间更新(x1,y1),(x2,y2) 
//(x1<=x2,y1<=y2)(与建立坐标系有关,我建立在第一象限)
update(x1,y1);
update(x1,y2+1);
update(x2+1,y1);
update(x2+1,y2+1);
//单点查询直接查询即可

二维 区间更新 区间查询

我没有找到模板题目,只能在这里详细说说了。(感兴趣可以自己找线段树的题用树状数组写)
我找到一个很不错的博客:(这个可能看似和我之前讲的一维区间更新、区间查询不太一样,但仔细思考一下会发现是一样的)http://www.cnblogs.com/hefenghhhh/p/6554594.html

我们知道一维区间更新是由一个差分数组实现的,对[s,t]区间增加x,利用差分的思想,只需要让delta[s]+x,delta[t+1]−x即可。其中delta[i]表示从i到n的区间的增量。
num'numdelta[i]insum(s,t)=ti=snum[i]+ti=s(delta[i](ti+1))
num'numdelta[i]inti=s(delta[i](ti+1))=(t+1)ti=sdelta[i]i=st(delta[i]i)
我们用两个树状数组,一个维护delta[i],一个维护delta[i]∗i,那么上面的区间修改和区间查询均可以在O(logN)的时间内完成了。

二维区间更新上面已经讲了,我们来看看区间查询。
设(x1,y1)和(x2,y2),(x1<=x2,y1<=y2)
sum(x1,y1,x2,y2)=sum(1,1,x2,y2)−sum(1,1,x2,y1−1)−sum(1,1,x1−1,y2)+sum(1,1,x1−1,y1−1)
那么我们只需知道如何查询类似区间sum(1,1,x1,y1)就可以了。
sum(1,1,x1,y1)=x1i=1y1j=1num[i][j]+x1i=1y1j=1delta[i][j](x1i+1)(y1j+1)


x1i=1y1j=1delta[i][j](x1i+1)(y1j+1)=(x1+1)(y1+1)x1i=1y1j=1delta[i][j](x1+1)x1i=1y1j=1jdelta[i][j](y1+1)x1i=1y1j=1idelta[i][j]+x1i=1y1j=1ijdelta[i][j]

所以我们维护四个树状数组即可delta[i][j],delta[i][j]∗i,delta[i][j]∗j,delta[i][j]∗i∗j
类似一维区间更新、区间查询里面维护两个数组。

你可能感兴趣的:(ACM,树状数组)