背景:
GDSOI
\text{GDSOI}
GDSOI
zsyz
\text{zsyz}
zsyz全挂,
OZYrank16
\text{OZYrank16}
OZYrank16卡线没进。
看了师兄们的游记,发现一些好东西没学。
正题:
先考虑容斥的做法。
一维的做法:
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
二维的做法:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
三维的做法:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=q;k++)
sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]+sum[i][j][k-1]
-sum[i-1][j-1][k]-sum[i-1][j][k-1]-sum[i][j-1][k-1]
+sum[i-1][j-1][k-1]+a[i][j][k];
.
.
.
...
...
假设维数为
T
T
T。
如此的时间复杂度是
Θ
(
n
T
∗
2
T
)
\Theta(n^T*2^T)
Θ(nT∗2T)的。
很不优秀啊。
其实有更好的做法。
一维的做法:
for(int i=1;i<=n;i++)
a[i]+=a[i-1];
二维的做法:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]+=a[i-1][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]+=a[i][j-1];
三维的做法:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=q;k++)
a[i][j][k]+=a[i-1][j][k];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=q;k++)
a[i][j][k]+=a[i][j-1][k];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=q;k++)
a[i][j][k]+=a[i][j][k-1];
.
.
.
...
...
其实很好理解。
你考虑先解决一维,再将这个结果继承到下一维,如此类推。
时间复杂度:
Θ
(
T
n
T
)
\Theta(Tn^T)
Θ(TnT)。
利用这个东西,就可以算出高维前缀和。高维前缀和一般都是
n
=
2
n=2
n=2的情况,一般来说就是求一个集合的子集这类的东西。
over.
\text{over.}
over.