SPOJ GSS4 洛谷P4514上帝造题的七分钟&&[树状数组进阶]

树状数组大法好

讲这道题之前先讲点进阶内容

一维树状数组的区间修改+区间求和

不会树状数组入门知识的->出门左转
不会树状数组单点修改的->出门右转
好了,现在留下的都是奆佬
我们先讲一下区间修改
根据之前单点修改,区间求和的思想,发现差分数组非常有用,那么我们不难发现一个有趣的性质,对于差分数组d[i],原数组a[i],存在 a [ i ] = ∑ k = 1 i d [ i ] a[i]=\sum\limits_{k=1}^{i}d[i] a[i]=k=1id[i],自己手动模拟一下 , d [ 1 ] = a [ 1 ] d[1]=a[1] d[1]=a[1] d [ 2 ] = a [ 2 ] − a [ 1 ] = d [ 1 ] + d [ 2 ] = a [ 2 ] − a [ 1 ] + a [ 1 ] d[2]=a[2]-a[1]=d[1]+d[2]=a[2]-a[1]+a[1] d[2]=a[2]a[1]=d[1]+d[2]=a[2]a[1]+a[1],后面类似,所以我们求前缀和就可以 s [ i ] = ∑ k = 1 i a [ i ] = ∑ k = 1 i ∑ j = 1 k d [ j ] s[i]=\sum\limits_{k=1}^ia[i]=\sum\limits_{k=1}^{i}\sum\limits_{j=1}^{k}d[j] s[i]=k=1ia[i]=k=1ij=1kd[j]
复杂度爆表,然后我们不难发现, d [ 1 ] d[1] d[1]被累加了i次, d [ 2 ] d[2] d[2]被累加了i-1次,于是我们就可以把上面 n 2 n^2 n2的式子写成 s [ i ] = ∑ k = 1 i ∑ j = 1 k d [ j ] = ∑ k = 1 i d [ k ] ∗ ( i − k + 1 ) = ( i + 1 ) ∗ ∑ k = 1 i d [ k ] − ∑ k = 1 i d [ k ] ∗ k s[i]=\sum\limits_{k=1}^{i}\sum\limits_{j=1}^{k}d[j]=\sum\limits_{k=1}^{i}d[k] * (i-k+1)=(i+1) * \sum\limits_{k=1}^{i}d[k] - \sum\limits_{k=1}^{i}d[k] * k s[i]=k=1ij=1kd[j]=k=1id[k](ik+1)=(i+1)k=1id[k]k=1id[k]k,于是我们就可以用树状数组维护两个数组,一个是 d [ i ] d[i] d[i],另一个是 d [ i ] ∗ i d[i]*i d[i]i,然后查询的时候就套上面的式子就好了,是不是比线段树简单多了

二维树状数组

类比一维树状数组,一维树状数组数组上,前缀和是每次减个lowbit的位置加起来,那么我们放在二维上,我们可以想象为先横着求和,再竖着求和,所以是

for (int i=x;i;i-=lowbit(i)
for (int k=y;k;k-=lowbit(k)

这样就可以得到前缀和了,但是我要求你有修改操作
类比一下一维树状数组的差分性质,我们对它做一个二维差分,想一下二维前缀和,i,k的二维前缀和和等于 s [ i ] [ k ] = s [ i − 1 ] [ k ] + s [ i ] [ k − 1 ] − s [ i − 1 ] [ k − 1 ] + a [ i ] [ k ] s[i][k]=s[i-1][k]+s[i][k-1]-s[i-1][k-1]+a[i][k] s[i][k]=s[i1][k]+s[i][k1]s[i1][k1]+a[i][k],于是我们就类比了一下,我们使得差分数组 d [ i ] [ k ] = a [ i ] [ k ] − a [ i − 1 ] [ k ] − a [ i ] [ k − 1 ] + a [ i − 1 ] [ k − 1 ] d[i][k]=a[i][k]-a[i-1][k]-a[i][k-1]+a[i-1][k-1] d[i][k]=a[i][k]a[i1][k]a[i][k1]+a[i1][k1],然后 d [ i ] [ k ] d[i][k] d[i][k]也存在类似于一维的性质 d [ i ] [ k ] = ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] d[i][k]=\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y] d[i][k]=x=1iy=1kd[x][y],手模一下就发现了,或者根据差分的过程也能得到,所以前缀和就是 s [ i ] [ k ] = ∑ j = 1 i ∑ l = 1 k ∑ x = 1 j ∑ y = 1 l d [ x ] [ y ] s[i][k]=\sum\limits_{j=1}^{i}\sum\limits_{l=1}^{k}\sum\limits_{x=1}^{j}\sum\limits_{y=1}^{l}d[x][y] s[i][k]=j=1il=1kx=1jy=1ld[x][y],复杂度炸了一地啊!然后开始优化,发现 d [ 1 ] [ 1 ] d[1][1] d[1][1]用了 i ∗ k i * k ik次, d [ 2 ] [ 2 ] d[2][2] d[2][2]用了 i ∗ ( k − 1 ) i*(k-1) i(k1)次,所以得到对于差分数组里的 d [ x ] [ y ] d[x][y] d[x][y]用了 ( i − x + 1 ) ∗ ( k − y + 1 ) (i-x+1)*(k-y+1) (ix+1)(ky+1)次,化简上面式子 s [ i ] [ k ] = ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ ( i − x + 1 ) ∗ ( k − y + 1 ) s[i][k]=\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*(i-x+1)*(k-y+1) s[i][k]=x=1iy=1kd[x][y](ix+1)(ky+1),还是没办法直接维护,继续化简一下?
= ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ ( i − x + 1 ) ∗ ( k − y + 1 ) =\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*(i-x+1)*(k-y+1) =x=1iy=1kd[x][y](ix+1)(ky+1)
= ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ ( i + 1 ) ∗ ( k + 1 − y ) − ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ x ∗ ( k + 1 − y ) =\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*(i+1)*(k+1-y)-\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*x*(k+1-y) =x=1iy=1kd[x][y](i+1)(k+1y)x=1iy=1kd[x][y]x(k+1y)
= ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ ( i + 1 ) ∗ ( k + 1 ) − ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ ( i + 1 ) ∗ y − ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ x ∗ ( k + 1 ) + ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ x ∗ y =\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*(i+1)*(k+1)-\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*(i+1)*y-\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*x*(k+1)+\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*x*y =x=1iy=1kd[x][y](i+1)(k+1)x=1iy=1kd[x][y](i+1)yx=1iy=1kd[x][y]x(k+1)+x=1iy=1kd[x][y]xy
整理一下得到
( i + 1 ) ∗ ( k + 1 ) ∗ ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] − ( i + 1 ) ∗ ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ y − ( k + 1 ) ∗ ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ x + ∑ x = 1 i ∑ y = 1 k d [ x ] [ y ] ∗ x ∗ y (i+1)*(k+1)*\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]-(i+1)*\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*y-(k+1)*\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*x+\sum\limits_{x=1}^{i}\sum\limits_{y=1}^{k}d[x][y]*x*y (i+1)(k+1)x=1iy=1kd[x][y](i+1)x=1iy=1kd[x][y]y(k+1)x=1iy=1kd[x][y]x+x=1iy=1kd[x][y]xy
这就好维护了,我们维护四个二维树状数组, d [ i ] [ k ] d[i][k] d[i][k] d [ i ] [ k ] ∗ i d[i][k]*i d[i][k]i d [ i ] ] [ k ] ∗ k d[i]][k]*k d[i]][k]k d [ i ] [ k ] ∗ i ∗ k d[i][k]*i*k d[i][k]ik,然后就没有然后了。
注意
二维差分的时候若对矩形{x1,y1}{x2,y2}进行加值,那么在差分数组上的表现为 d [ x 1 ] [ y 1 ] + = v a l d[x1][y1]+=val d[x1][y1]+=val d [ x 2 + 1 ] [ y 2 + 1 ] + = v a l d[x2+1][y2+1]+=val d[x2+1][y2+1]+=val d [ x 2 + 1 ] [ y 1 ] − = v a l d[x2+1][y1]-=val d[x2+1][y1]=val d [ x 1 ] [ y 2 + 1 ] − = v a l d[x1][y2+1]-=val d[x1][y2+1]=val,为啥?自己手模滑稽

所以这道题就是裸题

代码

//By AcerMo
#include
#include
#include
#include
#include
#define lowbit(x) x&(-x)
using namespace std;
const int M=2500;
int n,m;
int s1[M][M],s2[M][M],s3[M][M],s4[M][M];
inline void read(int &x)
{
	x=0;int f=1;char ch=getchar();
	while (!isdigit(ch)) ch=='-'?f=0:f=1,ch=getchar();
	while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	if (!f) x*=-1;
}
inline void write(int x)
{
	if (x<0) putchar('-'),x=-x;
	if (x>9) write(x/10);
	putchar(x%10+'0');
	return ;
}
inline void add(int x,int y,int z)
{
	for (int i=x;i<=n;i+=lowbit(i))
	for (int k=y;k<=m;k+=lowbit(k))
	s1[i][k]+=z,s4[i][k]+=z*x*y,
	s3[i][k]+=z*y,s2[i][k]+=z*x;
	return ;
}
inline int sum(int x,int y)
{
	int a1=0,a2=0,a3=0,a4=0;
	for (int i=x;i;i-=lowbit(i))
	for (int k=y;k;k-=lowbit(k))
	a1+=s1[i][k],a2+=s2[i][k],a3+=s3[i][k],a4+=s4[i][k];
	a1*=(x+1)*(y+1);a2*=(y+1);a3*=(x+1);
	a1-=a2;a1-=a3;a1+=a4;
	return a1;
}
signed main()
{
	char c=getchar();
	read(n);read(m);
	int a,b,x,y,z;
	while (cin>>c)
	{
		read(a);read(b);read(x);read(y);
		if (c=='L') 
		{
			read(z);
			add(a,b,z);add(x+1,y+1,z);
			add(a,y+1,-z);add(x+1,b,-z);
		}
		else
		{
			int a1=sum(x,y),a2=sum(a-1,b-1);
			int a3=sum(x,b-1),a4=sum(a-1,y);
			write(a1+a2-a3-a4);puts("");
		}
	}
	return 0;
}

你可能感兴趣的:(差分,数据结构-树状数组)