2025.7.5 前缀和
前缀和
常用于区间求和,对于长度为 \(n\) 的连续区间求和,时间复杂度从 \(O(n)\) 降到 \(O(1)\) 。
实现
构造
for(int i=1;i<=n;i++){
f[i]=f[i-1]+a[i];
}
查询
\(\sum_{i=l}^{r}a_i=f_r-f_{l-1}\) 即
int sum1=0,sum2=0;
for(int i=l;i<=r;i++){
sum1+=a[i];
}
sum2=f[r]-f[l-1];
//sum1==sum2
例题P8865
题目描述
花园可以看作有 \(n*m\) 个位置的网格图,从上到下分别为第 \(1\) 到第 \(n\) 行,从左到右分别为第 \(1\) 列到第 \(m\) 列,其中每个位置有可能是土坑,也有可能不是,可以用 \(a_{ij}\) 表示第 \(i\) 行第 \(j\) 列这个位置有土坑,否则用 \(a_{ij}\) 表示这个位置没土坑。
一种种花方案被称为 \(C\) 型的,如果存在 \(x_1,x_2\in[1,n]\) ,以及 \(y_0,y_1,y_2\in[1,m]\) ,满足 \(x_1+1<x_2\) ,并且 \(y_0<y_1,y_2\leq{m}\) ,使得第 \(x_1\) 行的第 \(y_0\) 到第 \(y_1\) 列、第 \(x_2\) 行的第 \(y_0\) 到第 \(y_2\) 列以及第 \(y_0\) 列的第 \(x_1\) 到第 \(x_2\) 行都不为土坑,且只在上述这些位置上种花。
一种种花方案被称为 \(F\) 形的,如果存在 \(x_1,x_2,x_3\in[1,n]\) ,以及 \(y_0,y_1,y_2\in [1,m]\) ,满足 \(x_1+1<x_2<x_3\) ,并且 \(y_0<y_1,y_2\leq{m}\) ,使得第 \(x_1\) 行的第 \(y_0\) 到第 \(y_1\) 列、第 \(x_2\) 行的第 $y_0 $到第 \(y_2\) 列以及第 \(y_0\) 列的第 \(x_1\) 到第 \(x_3\) 行都不为土坑,且只在上述这些位置上种花。
现在小 C 想知道,给定 \(n,m\) 以及表示每个位置是否为土坑的值 \(a_{ij}\) , \(C\) 形和 \(F\) 形种花方案分别有多少种可能?由于答案可能非常之大,你只需要输出其对 998244353 取模的结果即可,具体输出结果请看输出格式部分。
[!NOTE]
对于所有数据,保证:\(1\leq{T}\leq5\) , \(1\leq{n},m\leq10^3\) ,\(0\leq{c},f\leq1\) ,\(a_{ij}\in\{0,1\}\) 。
思路
首先考虑暴力:枚举同一列不同行且间隔行数至少为一的两个点,时间复杂度为 \(O(n^3)\) ;再横着分别枚举两个点右侧有几个连续不为“坑”的格数,时间复杂度再乘 \(O(n)\) 。即使用前缀和可以把右侧连续不为“坑”个数计算的时间复杂度降到 \(O(1)\) ,但 \(O(n^3)\) 的时间复杂度仍不可接受。
我们实际可接受的时间复杂度为 \(O(n^2)\) ,所以至少消掉一个 \(O(n)\) 。
对于每一个 \(C\) 型,我们并不关心之前的行数具体如何分布,我们只在意在两行之前横向的有几种选择方案,在乘上当前一行的方案数。
对于每一个 \(F\) 型,我们可以把它看作一个 \(C\) 型加上“竖”,所以只要知道在当前行之前有几个 \(C\) 型。
[!IMPORTANT]
如果竖着枚举时发现有“坑”,要把之前的选择方案清空!
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1005,mod=998244353;
int t,id,n,m;
int h[N][N],l[N][N];
long long c,f,ansc,ansf;
string s;
bool a[N][N];
int main(){
scanf("%d%d",&t,&id);
while(t--){
memset(h,0,sizeof(h));
memset(l,0,sizeof(l));
ansc=ansf=0;
scanf("%d%d%lld%lld",&n,&m,&c,&f);
for(int i=1;i<=n;i++){
cin>>s;
for(int j=1;j<=m;j++){
a[i][j]=1-s[j-1]+'0';//个人习惯,“1”表示可以种花
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
if(a[i][j]){//不为“坑”
l[i][j]=max(i,l[i+1][j]);
h[i][j]=max(j,h[i][j+1]);//奇特的"前缀和",h[i][j]表示第i行的j~h[i][j]都可种花
}
else{
l[i][j]=h[i][j]=-1;//为“坑”
}
}
}
for(int j=1;j<m;j++){
long long ll=0,p=0;//ll:之前的横向选择数,p:前i-1行的“C”型个数
for(int i=1;i<=n;i++){
if(!a[i][j]){
ll=p=0;//清零
continue;
}
long long x=h[i][j]-j;//当前行的选择方案数
ansc+=(x*ll)%mod;
ansc%=mod;
ansf+=p%mod;
ansf%=mod;
p+=x*ll%mod;//累计“C”型个数
p%=mod;
ll+=max(0,h[i-1][j]-j);
}
}
printf("%lld %lld\n",ansc*c,ansf*f);
}
return 0;
}
完结撒花!!!

浙公网安备 33010602011771号