当前位置: 首页 > wzjs >正文

公司如何申请一个网站新东方培训机构官网

公司如何申请一个网站,新东方培训机构官网,售后好的品牌策划公司,青岛知名网站建设定制目录 引入 基本介绍 例题1 题目描述: 分析1 同余原理 代码1 分析2 1. 初始化 cnt[0] 1 的原因 2. 遍历时实时统计的逻辑 代码2: 例题2 问题描述: 思路1分析 思路1代码: 思路2分析: 代码2 二维前缀…

目录

引入

基本介绍

例题1

题目描述:

分析1

同余原理

代码1

分析2

1. 初始化 cnt[0] = 1 的原因

2. 遍历时实时统计的逻辑

代码2:

例题2

问题描述:

思路1分析

思路1代码:

思路2分析: 

代码2

二维前缀和

问题1:

问题描述:

数据范围:

分析:

1.考虑最直观的暴力解法(❌)

2. 优化

3. 前缀和

二维前缀和(进阶前缀和)

4. 那我们了解过二位前缀和之后顺理成章可以在这个题里应用

1. 建立价值地图

步骤2. 计算二维前缀和

步骤3. 计算任意正方形区域的和

下面让我们——处理一些容易出错的地方,并给出代码

1. 处理坐标偏移

2. 计算前缀和

3. 遍历所有可能的正方形

代码:

总结

一、核心思想

二、典型应用场景

三、一些小tips

四、代码实现要点

五、最后总结


引入

输入一个长度为 n 的整数序列。

接下来再输入 m 个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

观察上面的要求,输入一个长度为n的整数序列,根据输入的询问,输出从第l个数到第r个数的和。

如果遍历后一个一个加,在数据很大的情况下,会超时。

那么怎么做才可以简化代码呢?

——用前缀和

基本介绍

前缀和,这三个字,顾名思义表示是一个和,并且是某段序列的前缀的和。

我们回到上一个题,如果我们提前知道第1个数到l-1个数的和,还有第1个数到第r个数的和,那么它们相减是不是可以直接得出结果,这个时间复杂度就是O(N)了;

那么如何得出从第一个数到某个数的和呢?(也就是前缀和的具体做法

先定义一个数组s[N],s[i]表示从第一个数字到第i个数字的和。

我们输入一段长度为n的序列,如果是一个个输入的,那么每输入一个就加到s[i]上,并且s[i]还需要加上s[i-1]

则代码为:

s[0]=0;
for(int i=1;i<=n;i++)
{cin>>a[i];s[i]=a[i]+s[i-1];
}

⭐前缀和有个很重要的部分: 由于s[i]需要去加上s[i-1]。为了使数组不访问到未知区域,所以s[i]中的i都需要从1开始,如果输入的a[i]输入必须要从0开始,那么代码就需要变成s[i+1]=a[i]+s[i]

🆗

现在基本了解了前缀和的思路,那么先做个例题来写一下吧

例题1

题目描述:

分析1

在题目中可看出来,它要求计算Ai到Aj的和是否是K倍的,

那根据上面介绍的前缀和思想,我们很容易能想出做题方法。

首先定义两个数组长度为N的最大值(100010)多一个10,这样比较保险。

一个数组是用于存储长度为n的数组a[N],一个存储前缀和s[N].

⭐注意,我们定义完后,需要明晰前缀和数组s[i]表示的是什么意思——s[i]表示从1开始到第i个元素的总和。

求出s[i]数组后,我们现在需要解决输出问题——我们需要输出区间内的和为K倍的区间数量。

很容易的能想到for循环嵌套,去一一遍历,但由于数据范围是10的5次方,n的平方的时间复杂度显然是过不去的。所以需要另寻方法

这里引入一个数学上的(同余原理

同余原理

如果想让(s[i]-s[j-1]被k整除,等同于,s[i]取余k等于s[j-1]取余k。

那么为了方便,我们在输入a[i]数组,给s[i]赋值的同时,直接取余k,

那这时候可能会有人疑惑了,取余k之后,去判断前缀和数组的两个数是否相等不还是需要两个for循环嵌套吗

这里就需要借用另一个数组来实现了,我们定义一个st[N]数组,

它表示  s数组中取余k后结果相同的  数量

表示形式为——st[s[i]],这个就可以表示与s[i]取余k相等的数量

⭐注意,1个数也可以当数组,所以不用去掉s[i]本身的数量

而取余0也就是st[0]表示:前缀和取模后余数为 0 的次数。每个余数为 0 的前缀和本身对应一个 从起点到当前位置的区间,这些区间的和本身就是 K 的倍数。

而且st[s[i]],它最终要算的次数是从这个结果里面,拿出两个数作为区间端点,也就是C(m,2),从m个数任意挑选两个数字,表示为:(st[i]*(st[i]-1))/2

分析完了,现在可以写出代码了。

代码1

#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;
#define N 100010long long s[N],st[N];
int a;
int flag;int main()
{int n,k;scanf("%d%d",&n,&k);for(int i=1;i<=n;i++){scanf("%d",&a);s[i]=(s[i-1]+a)%k;st[s[i]]++;}long long res=st[0];for(int i=0;i<k;i++)//取余结果一定不大于k{res+=st[i] * (st[i] - 1) / 2;}cout<<res<<endl;return 0;
}

代码做了一些优化,把数组变为了一个int变量,输入这个数的时候同时计算s[i]和st[i]的值,注意数值范围数组是long long 。

分析2

或者可以借用另一种方法,把st[0]设置为1,然后每次算完s[i]之后,就加上st[s[i]],再让st[s[i]]加加。

这个思路是怎么想的呢?

利用前缀和的同余性质,动态维护余数出现次数,并实时统计所有可能的有效区间

1. 初始化 cnt[0] = 1 的原因
  • 虚拟前缀和:引入一个不存在的 s[0] = 0(对应空数组的和)。

    • 作用:当某个实际前缀和 s[i] ≡ 0 (mod K) 时,区间 [1, i] 的和自动满足 s[i] - s[0] ≡ 0 (mod K),即该区间有效。
    • 示例:若 A = [3]K = 3,则 s[1] = 0,此时 s[1] - s[0] = 0,区间 [1,1] 有效。
  • 避免漏算:如果没有这个虚拟的 s[0],当 s[i] ≡ 0 时,无法统计从数组开头到 i 的区间。

2. 遍历时实时统计的逻辑
  • 操作步骤:
    对每个 i(从 1 到 N):

    1. 计算 s[i] = (s[i-1] + A[i]) % K
    2. 立即将 cnt[s[i]] 累加到结果:
  • 当前 s[i] 的余数为 r,之前所有余数为 r 的前缀和(即 cnt[r] 次)均可与当前 s[i] 形成有效区间。
    3. 再更新 cnt[s[i]]:将当前余数 r 的计数加 1,供后续前缀和匹配。

  • 动态维护的直观解释:

    • 统计的是历史匹配次数:每次处理 s[i] 时,cnt[r] 表示在 i 之前,余数为 r 的前缀和已经出现的次数。
    • 组合数的实时计算:每个新余数 r 贡献 cnt[r] 个新区间,最终所有余数 r 的总贡献为 Σ cnt[r] * (cnt[r]-1)/2(但通过动态累加避免了显式计算组合数)。

假如s[1]是1,而s[2]也是1,第一次的时候由于s[1]无法和别人组成区间,所以res+=0;而当到s[2]时,由于此时cnt[1]加加过了,所以为1,res+=1;表示1,2组成一个区间。

也就是位置2到位置2的和属于是K倍区间的情况。

代码2:

#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;
#define N 100010long long s[N],st[N];
int a;
int flag;int main()
{int n,k;scanf("%d%d",&n,&k);st[0]=1;//这样从头开始到第i个位置的这种情况就不会被漏掉。//它表示s[i]=0,也就是这个和本身就是K倍。for(int i=1;i<=n;i++){scanf("%d",&a);s[i]=(s[i-1]+a)%k;res+=st[s[i]];st[s[i]]++;}cout<<res<<endl;return 0;
}

例题2

问题描述:

思路1分析

分析一下这个题目,有三个数组A、B、C,他们长度为N,问有多少三人组,是第一个数组A是最小的,第二个数组B大小属于中间,第三个数组C是最大的。

很好想到的思路是遍历三个数组,嵌套3个for循环,但是我们看一下数据范围:

显而易见超时了,10的5次方只允许有一次for循环,那么如何优化才可以不超时输出正确结果呢?

既然只能支持一次for循环,那么如果只能挑选一个数组去循环的话,选哪个呢?

选A和C的话,把最大或者最小的先定下来,但是另外两个仍然属于不固定的状态

那么遍历B,遍历这个大小处于中间的值,从A中选择大小小于B的,从C中选择大小大于C的,

显而易见可以用二分,先排序好数组A和C,然后遍历B,利用二分求出A中小于B的,以及C中大于B的。A中小于B的乘以C中大于B的,再把所有结果相加即可。

既然按照这个思路分析完了,那么下面就是展示代码了:

思路1代码:

#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;const int N=10e5+10;
int a[N],b[N],c[N];
int n;int main()
{scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&a[i]);}for(int i=0;i<n;i++){scanf("%d",&b[i]);}for(int i=0;i<n;i++){scanf("%d",&c[i]);}sort(a,a+n);sort(c,c+n);long long res=0;for(int i=0;i<n;i++){int l=-1;int r=n;while((l+1)!=r){int mid=l+r>>1;if(a[mid]<b[i])l=mid;else r=mid;}int p=l+1;//l从0开始的l=-1; r=n;while((l+1)!=r){int mid=l+r>>1;if(c[mid]<=b[i])l=mid;else r=mid;}int q=n-l-1;res+=(long long)p*q;//cout<<p<<" "<<q<<endl;}cout<<res<<endl;return 0;
}

思路2分析: 

那么有什么更简便的方法吗?

YES,回归正题,这题可以用前缀和。

注意:前缀和只是个思想,不要把它禁锢在只能计算数组里的一段数字的和的情况。

这个题的前缀和就不是很好想了,那么开始分析一下该如何优化吧

根据之前的思路,已知B[j],我们需要求出小于B[j]的所有A[i]和C[i],假设我们用一个数组存储小于B[j]的数量,但是要提前将数组A中所有元素出现的次数存入一个哈希表中,

最后即可以快速查找小于B[i]的所有元素的总数——只需要在枚举之前先将求出这个哈希表中各数的前缀和。

显而易见,需要两个数组,(对于A而言)

那么一共需要四个数组。

cnt[N]和s[N],

cnt[i]代表等于i的数有多少个?

s[i]表示从等于1到i的总共数量。——》如果所以如果要求小于B[i]的数量,只需要知道s[i-1]即可。

理论成立,下面开始实施

代码2

#include<iostream>
#include<algorithm>
#include<cstring>using namespace std;const int N=10e5+10;
int a[N],b[N],c[N];
int n;long long cnt1[N],cnt2[N];
long long s1[N],s2[N];void sr(int *p,long long *cnt)
{for(int i=0;i<n;i++){scanf("%d",&p[i]);p[i]++;cnt[p[i]]++;//由于p[i]从0开始计数,而且有i-1的情况,所以为了不存在-1越界数组的情况,范围变成1到10的5次方+1}
}int main()
{scanf("%d",&n);sr(a,cnt1);for(int i=0;i<n;i++){scanf("%d",&b[i]);b[i]++;}sr(c,cnt2);long long res=0;for(int i=1;i<=N;i++){s1[i]=cnt1[i]+s1[i-1];//cnt表示等于i的数的次数,s表示从1到i,等于其中任何一个数的次数和s2[i]=cnt2[i]+s2[i-1];}for(int i=0;i<n;i++){res+=s1[b[i]-1]*(s2[N]-s2[b[i]]);}cout<<res<<endl;return 0;
}

其中体现了一个非常重要的有关前缀和的东西——当数组元素的大小从0开始时,如果需要数组元素作为下标的话,需要偏移——》因为前缀和需要使用加上s[i-1],如果从0开始,就需要访问越界部分。

当然记得更改所有的相关数组。如果你在这个代码里忘记b[i]++,那么会无法得出正确答案的。 

二维前缀和

刚才的所有问题都是一维层面的,现在需要去解决二维方面的前缀和

问题1:

问题描述:

数据范围:

分析:

我们需要找出一个 R×R的正方形区域,这个区域的边必须和坐标轴平行,使得这个区域内所有目标点的价值总和最大。目标点可能有多个出现在同一坐标位置。


1.考虑最直观的暴力解法(❌)

看完题目后很可能会想到这样的解法:

  1. 首先遍历地图上的每个可能位置作为正方形的左上角

  2. 对于每个位置,计算这个正方形内所有点的价值之和

  3. 记录最大值

按照最大值来计算——假设地图范围是5000×5000,正方形边长是R=5000。那么需要计算的次数是:

  • 外层循环次数:(5000 - R + 1) × (5000 - R + 1) ≈ 2500万次

  • 内层每次计算需要遍历R×R次(假设R=5000,就是2500万次)
    总计算量达到 2500万 × 2500万 = 62.5万亿次!显而易见超时了。


2. 优化

我们需要找到一种方法,能够 快速计算任意矩形区域的和,而不是每次都重新遍历去暴力求解。这时,"前缀和"的概念就派上用场了。


3. 前缀和

说到前缀和,我们来回顾一下一维前缀和的一般使用方法

  • 一般情况下, S[i]通常表示前i个元素的和(从第1个到第i个),但是就如上面的第二题一样,不一定是这个含义,要根据题目意思来分析。

  • 计算区间[a,b]的和:S[b] - S[a-1]


二维前缀和(进阶前缀和)

现在问题扩展到二维。想象一个网格地图,每个格子有一个价值:

目标:快速计算任意矩形区域的和。例如下图中红色区域的和:

定义二维前缀和

  • S[i][j] 表示从左上角(1,1)(i,j)形成的矩形区域的总和

计算区域和公式
要计算区域[x1,y1][x2,y2]的和:

sum = S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1]

(⭐记得最后要加上被减了两次的左上角区域)


4. 那我们了解过二位前缀和之后顺理成章可以在这个题里应用

1. 建立价值地图
  1. 创建一个二维数组g[][],其中g[x][y]表示坐标(x,y)处所有目标点的价值总和(可能有多个目标点在此处)

  2. 将输入的坐标转换为数组索引(注意题目中坐标从0开始,但通常处理时会让数组索引从1开始,避免边界问题)

示例
假设输入三个目标点.

(0,0) 价值5
(1,1) 价值3
(1,1) 价值2

则:

  • g[1][1] = 5(坐标(0,0) → 数组索引(1,1))

  • g[2][2] = 3+2=5(坐标(1,1) → 数组索引(2,2))


步骤2. 计算二维前缀和

根据定义,计算每个S[i][j]

S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + g[i][j]

(当前格子的前缀和 = 上方前缀和 + 左方前缀和 - 左上角前缀和 + 当前价值)


步骤3. 计算任意正方形区域的和

假设炸弹覆盖的右下角在(i,j),边长为R,则这个正方形的左上角是(i-R+1, j-R+1)。区域和为:

sum = S[i][j] - S[i-R][j] - S[i][j-R] + S[i-R][j-R]

示例
假设R=2,计算以(3,3)为右下角的2×2区域和:

sum = S[3][3] - S[1][3] - S[3][1] + S[1][1]

下面让我们——处理一些容易出错的地方,并给出代码

1. 处理坐标偏移
int x,y,w;
cin >> x >> y >> w;
x++; y++; // 将坐标转换为1-based索引
g[x][y] += w; // 累加同一位置的价值
  • 原题坐标范围是0~5000 → 转换为1~5001,避免处理负数索引

2. 计算前缀和
for(int i=1; i<=a; i++)for(int j=1; j<=b; j++)s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
  • 这里s数组既存储原始价值,又存储前缀和(节省内存)

3. 遍历所有可能的正方形
for(int i=r; i<=a; i++)for(int j=r; j<=b; j++)res = max(res, s[i][j] - s[i-r][j] - s[i][j-r] + s[i-r][j-r]);
  • 枚举正方形的右下角坐标(i,j)

  • 通过前缀和公式快速计算区域和

代码:

#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;const int N=5010;int a,b;
int s[N][N];//原数组,后面可以变成二维数组存储前缀和int main()
{int n,r;cin>>n>>r;r=min(5001,r);a=b=r;while(n--){int x,y,w;cin>>x>>y>>w;x++;y++;a=max(a,x);b=max(b,y);s[x][y]+=w;}for(int i=1;i<=a;i++){for(int j=1;j<=b;j++){s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];//cout<<s[i][j]<<endl;}}int res=0;//枚举它,从右下角枚举所以初始坐标为rfor (int i = r; i <= a; i ++ ){for (int j = r; j <= b; j ++ ){res = max(res, s[i][j] - s[i - r][j] - s[i][j - r] + s[i - r][j - r]);}}cout<<res<<endl;return 0;
}

 

总结

前缀和思想是一种通过预处理数组来高效计算区间和的算法技巧,其核心在于通过预计算并存储前n项的和,将原本复杂的区间查询问题转化为简单的差值运算,从而大幅优化时间复杂度。以下是其详细用法总结:

一、核心思想

  1. 预处理数组:构建前缀和数组 sum,其中 sum[i] 表示原数组前i项的和。例如,原数组 a[1...n] 的前缀和数组满足 sum[i] = sum[i-1] + a[i],从而快速计算任意区间 [l, r] 的和为 sum[r] - sum[l-1] 。
  2. 优化时间复杂度:将暴力法的O(n²)或O(n³)复杂度降至O(n)预处理 + O(1)查询,适用于多次区间和查询的场景 。

二、典型应用场景

  1. 一维数组问题

    • 寻找中心索引:通过比较左侧前缀和与右侧(总和 - 左侧 - 当前元素)是否相等,快速定位中心索引 。
    • 和为K的子数组:利用哈希表记录前缀和出现的次数,统计满足 sum[j] - sum[i] = K 的子数组数量,将时间复杂度从O(n²)优化至O(n) 。
    • 统计特定条件的子数组:如优美子数组(含k个奇数)、和可被K整除的子数组,结合哈希表记录前缀奇数的数量或余数分布,实现高效统计 。
  2. 二维矩阵问题

    • 子矩阵和计算:构建二维前缀和矩阵 s[i][j],通过公式 s[i][j] = prefixSum[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j] 预处理,查询时用容斥原理计算子矩阵和 。

三、一些小tips

  1. 结合哈希表:在统计满足特定条件的子数组时,哈希表可存储前缀和(或余数、奇数个数等)的频率,避免双重循环遍历。例如,哈希表初始化需包含 hash[0] = 1,以处理前缀和直接等于目标值的情况 。
  2. 负数处理:当涉及余数计算(如和可被K整除)时,需将负数余数调整为正值。例如,(sum % K + K) % K 确保余数在 [0, K-1] 范围内 。
  3. 空间优化:无需显式构建前缀和数组时,可直接用变量累加前缀和,减少空间占用 。
  4. 扩展应用:前缀和思想可推广至其他运算(如异或、乘积)或变种问题(如前缀GCD、差分数组),例如统计区间异或结果或动态数组修改后的区间和 。

四、代码实现要点

  • 一维前缀和模板
  for (int i=1; i<=n; ++i) {sum[i] = sum[i-1] + arr[i];}// 查询区间[l, r]的和:sum[r] - sum[l-1]
  • 二维前缀和模板
for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) {s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];}}

五、最后总结

前缀和思想——关键在于灵活运用预处理数据与哈希映射,同时注意边界条件与负数处理,可以用在不同的算法题场景中。

http://www.dtcms.com/wzjs/377394.html

相关文章:

  • wordpress文章搬家seo资源
  • 网站有什么类型百度文库官网首页
  • 网站建设有哪些常用行为百度热线客服24小时
  • 长沙软件开发培训机构网站优化技术
  • 帮人做网站收费合法吗长沙优化网站厂家
  • 什么网站动物和人做的吗win10系统优化工具
  • c 做网站优点百度seo关键词优化电话
  • wordpress移动端顶部导航栏东莞网站推广优化公司
  • 简述网站内容如何优化网络营销推广方式
  • 怎样做好邯郸网站建设山东网站建设
  • 建筑模板是干什么用的防疫优化措施
  • 公司网站建设有哪些公司可以做线上推广渠道主要有哪些
  • 成都网站建设大公司营销模式有哪些
  • app开发培训课程seo基础教程
  • 设计官方网站2024很有可能再次封城吗
  • wordpress迁移器关键词优化怎么弄
  • 上海住房和城乡建设网站自制网站 免费
  • 网站备案 代办百度推广产品
  • 第一素材网深圳seo推广培训
  • 菠菜导航网站可以做樱花12e56
  • 爱奇艺会员做任务送十天网站qq刷赞网站推广快速
  • 手机网站和pc网站北京seo推广外包
  • 手机网站制作费百度知道小程序
  • 对网站建设的考核机制网页设计模板网站
  • 富顺网站建设网页是怎么制作的
  • 自己做的网站链接关键词seo优化公司
  • 铜陵公司做网站网站内部优化有哪些内容
  • 在织梦网站做静态网页影视网站怎么优化关键词排名
  • 建企业网站浩森宇特昆明网络推广方式有哪些
  • 网站建设 提升和扩大百度推广年费多少钱