树状数组相信大家都很熟悉了,而今天我将会为大家带了一些更加全面的操作,并且欢迎补充哦。
其实我想说的是,除了RMQ,线段树能做的,树状数组都能做。
换句话说,这是一个稍微进阶版的的树状数组,读者至少要会单调修改区间查询这个最基本的操作。
树状数组应该算是常数非常小的数据结构啦。而小生特别喜欢这个数据结构,这是因为它特别短,就是又短又快!
int lowbit(int x)
{
return x&-x;
}
这是树状数组的核心操作,看到了这个操作基本上就是树状数组了。
现在我们来看看这个操作的实际意义:
抛结论:令p=lowbit(x),则p在x在二进制下从右往左第一个1的位置上也为1,其它为0。
举个例子:lowbit( ( 110 )2 ) = (10)2
我觉得这个。。。很简单吧
这里就是利用差分的思想。
令a为原数组
令一个新的数组b:b[1]=a[1],b[i]=b[i]-b[i-1]。(差分数组)
那么a[i]=b[1]+b[2]+….+b[i]
这样我们用树状数组维护差分数组b即可,也就不维护原数组了,每次修改区间的时候只会有两个b被修改,因此可以在log级别完成查找与维护。
此外,由于任何一段b的区间和都可以写成两个a的差,因此一般情况下b的储存类型和a是相同的。
基于区间修改单点查询
我们看看前缀和怎么求,区间的话直接用前缀和减就可以了。
任然令a为原数组,b为差分数组
现在求a的前M项前缀和
a[1]+a[2]+a[3]+....+a[m]
=(b[1])+(b[1]+b[2])+(b[1]+b[2]+b[3])+...+(b[1]+b[2]+b[3]+...+b[m])
=m*b[1]+(m-1)*b[2]+...+1*b[m]
=m*(b[1]+b[2]+b[3]+...+b[m])-(0*b[1]+1*b[2]+2*b[3]+...+(m-1)*b[m])
显然的,我们再求一个数组c,表示(i-1)*b[i]
这样我们维护b数组和c数组的前缀和就可以实现a数组的区间修改和区间查询了。
表达式:suma(m)=m*sumb(m)-sumc(m)
这里维护b数组的前缀数组依旧可以和a的类型相同,但是维护c数组的数组多半需要开LL(极限不会超过n*a的)。
基于权值树状数组,要用到前面强调的lowbit操作。
根据一种很神奇的倍增思想。。。
就是根据倍增思想, 当我们把一个数加上2^k后(之前加的k都比当前k大),我们的c数组(树状数组),c[i+ 2^k]的值就是我们增加的这一段权值的个数(记住我们是权值树状数组哦)。就可以直接判断当前元素个数是否超过了K,超过了就退回去,否则就保留。
这种问题还去用线段树真是麻烦哎。
这么弱智就不贴了吧
这么弱智也不贴了吧
这么弱智。。。。
我贴。。。
struct BIT
{
int c1[maxn];
LL c2[maxn];
void Initial()
{
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
}
int lowbit(int x)
{
return x&(-x);
}
void update(int i,int j,int x)
{
LL ad1=1ll*(i-1)*x,ad2=1ll*j*x;
for(int t=i;t<=n;t+=lowbit(t))c1[t]+=x,c2[t]+=ad1;
for(int t=j+1;t<=n;t+=lowbit(t))c1[t]-=x,c2[t]-=ad2;
}
LL sum(int i)
{
LL ret=0;
for(int t=i;t;t-=lowbit(t))ret+=1ll*i*c1[t],ret-=c2[t];
return ret;
}
}bit;
果然短小精干吧,比线段树好多了吧。。(ZKW线段树除外,这两者本身就有密不可分的联系)
核心代码:
//这里是第K小元素
//oo=floor(log2(n))
int Kth(int k)
{
int ans=0,cnt=0;
for(int i=oo;i>=0;i--)
{
ans+=(1<if(ans>N || cnt+c[ans]>=k)ans-=(1<else cnt+=c[ans];
}
return ++ans;
}
代码注意:
- 这里cnt+c[ans]>=k返回是为了防止跳过,最后会停在正确答案的前一个位置,重复元素也不会影响结果。
- 最后还可以通过ans返回n+1来判断是否存在第K大元素