由数列的前n项和可知,s[i]=a[i]+s[i-1],如果我们要计算[l,r]的和,可用公式s[r]-s[l-1]!
#include
using namespace std;
const int N=100010;
int n,m;
int a[N],s[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=a[i]+s[i-1];
}
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",s[r]-s[l-1]);
}
return 0;
}
①如何计算前缀和矩阵(容斥原理)
s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j]
②如何利用前缀和矩阵,计算某一个子矩阵的和?
sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]
#include
#include
using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int n,m,q;
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j];
}
}
while(q--)
{
int x1,y1,x2,y2;
int sum=0;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
printf("%d\n",sum);
}
return 0;
}
此题和上面一题不一样的是,原来(x,y)为格子,而这里为点,所以在枚举长度为R的正方行的时候公式将改成s[i][j]-s[i-R][j]-s[i][j - R]+s[i-R][j-R](以(i,j)为右下角坐标开始枚举长度为R的正方形)
#include
#include
#include
using namespace std;
const int N = 5010;
int max_x, max_y;
int s[N][N];
int main()
{
int cnt,R;
cin>>cnt>>R;
R=min(5001,R);//爆炸范围超过5001时覆盖了所有目标范围,因此没必要考虑超出范围。
max_x = max_y = R;// 最小的爆炸计算范围是在一个最小爆炸范围之内计算
while(cnt--)
{
int x,y,w;
cin>>x>>y>>w;
x++,y++;//边界问题都至少从1开始
max_x=max(max_x,x),max_y=max(max_y,y);
s[x][y]+=w;//还解决了重复出现的情况
}
//预处理前缀和
for(int i=1;i<=max_x;i++)
for(int j=1;j<=max_y;j++)
s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1];
int res=0;
// 枚举所有边长是R的矩形,枚举(i, j)为右下角
for(int i=R;i<=max_x;i++)
for(int j=R;j<=max_y;j++)
res=max(res,s[i][j]-s[i-R][j]-s[i][j - R]+s[i-R][j-R]);
cout<
暴力枚举会超时所以需要优化!
线性同余定理:首先要知道一个定理, r % k 的余数 与 l % k 的余数相等, 那么( r - l) % k == 0
首先我们是从左依次往右, 判断 r 是否与 l(1 ~ r - 1) 的余数相等(不相等res+0), 如果相等, 则就要加上, 关键在于for循环里面的顺序, 首先要先加上cnt[a[i] % k ]的数量, 也就是除自己以外, 前面和自己余数相同的个数, 然后再进行cnt[a[i] % k] ++, 把自己也算进去, 这么依次递推。
对于cnt[0] = 1的理解:
对于余数非0的数而言,他的结果一定是从0加到总数减1,对于余数是0本身的数,他自身就可以作为一个结果,因此要初始化为1,不需要等下一个区间同余出现再加1!
#include
using namespace std;
typedef long long LL;
const int N=100010;
int n,k;
LL s[N],cnt[N];
int main()
{
scanf("%d%d",&n,&k);
for (int i = 1; i <= n; i ++ )
{
scanf("%lld", &s[i]);
s[i] += s[i - 1];
}
LL count=0;
cnt[0]=1;//不需要下一个区间出现再加1,本身就能直接加1
for(int i=1;i<=n;i++)
{
count+=cnt[s[i]%k];
cnt[s[i]%k]++;
}
cout<