树状数组模板
单点修改,区间询问模板
add(x,k)
,相当于 a[x]+=k
get(x)
相当于 pre[x]
int c[MAXN];
inline int lowbit(const int &x){return x&-x;}
void add(int x,int k)
{
while(x<=n)
{
c[x]+=k;
x+=lowbit(x);
}
}
int get(int x)
{
int ret=0;
while(x)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void init()
{
int x;
for(int i=1;i<=n;i++)
{
c[i]+=a[i];
x=i+lowbit(i);
if(x<=n) c[x]+=c[i];
}
}
区间修改,单点查询模板
运用了差分大法
add(l,r,x)
,是给 a[l]
到 a[r]
的所有数加 x
get(x)
,是查询 a[x]
的值
int c[MAXN];
inline int lowbit(const int &x){return x&-x;}
void repreadd(int x,int k)
{
while(x<=n)
{
c[x]+=k;
x+=lowbit(x);
}
}
void add(int l,int r,int x)
{
add(l,x),add(r+1,-x);
}
int get(int x)
{
int ret=0;
while(x)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void init()
{
int x;
for(int i=1;i<=n;i++)
{
c[i]+=a[i]-a[i-1];
x=i+lowbit(i);
if(x<=n) c[x]+=c[i];
}
}
区间修改,区间查询模板
说实话,这种用法算不上模板,但确实很强,比用线段树快很多,空间少用了很多,最重要的是:
代码量极其之短,而且特别好调(只要公式没写错)
设 \(a_i\) 为原数组, \(b_i\) 为 \(a_i\) 的差分数组
可得:
\[a_i=\sum\limits_{j=1}^ib_j
\]
因此,如果我们想要求出前 \(x\) 个数之和,可得:
\[\sum\limits_{i=1}^xa_i\\
=\sum\limits_{i=1}^x\sum\limits_{j=1}^ib_j\\
=b_1\times x+b_2\times(x-1)+b_3\times(x-2)+\cdots+b_x\times1\\
=\sum\limits_{i=1}^x(x-i+1)b_i\\
=(x+1)\sum\limits_{i=1}^xb_i-\sum\limits_{i=1}^xi \times b_i
\]
这时候就可以发现,要想求出区间之和,需要维护两个树状数组,一个统计 \(b_i\) 的前缀和,另一个统计 \(i \times b_i\) 的前缀和。
int n;
ll c1[MAXN],c2[MAXN],a[MAXN];
inline int lowbit(int x){return x&-x;}
void stdadd(int x,ll k)
{
ll w=x*k;
while(x<=n)
{
c1[x]+=k,c2[x]+=w;
x+=lowbit(x);
}
}
ll stdgetsum(ll *c,int x)
{
ll ret=0;
while(x)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void add(int l,int r,int x)
{
stdadd(l,x);
stdadd(r+1,-x);
}
ll getsum(int l,int r)
{
return (r+1)*stdgetsum(c1,r)-l*stdgetsum(c1,l-1)-
stdgetsum(c2,r)+stdgetsum(c2,l-1);
}
void init()
{
for(int i=1;i<=n;i++)
{
c1[i]+=a[i]-a[i-1];
c2[i]+=i*(a[i]-a[i-1]);
int x=i+lowbit(i);
if(x<=n)
{
c1[x]+=c1[i];
c2[x]+=c2[i];
}
}
}