前缀和定义
例如:
1 2 3 4 5 6 7
| vector<int> pre(n+1, 0); for (int i = 1; i <= n; i++) { pre[i] = pre[i-1] + a[i]; }
int sum = pre[r] - pre[l-1];
|
2. 二维前缀和
用于计算矩阵的和$\Sigma{i=1}^n\Sigma{j=1}^na_{ij}$
1 2 3 4 5 6 7 8
| vector<vector<int>> pre(m+1, vector<int>(n+1, 0)); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { pre[i][j] = a[i][j] + pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1]; } }
int sum = pre[x2][y2] - pre[x1-1][y2] - pre[x2][y1-1] + pre[x1-1][y1-1];
|
前面两个的例题比较多,则行里就不举例了。
3. 前缀和与哈希表结合
这是最常用的变种,用于解决”区间和为k的区间个数”问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int countSubarrays(vector<int>& nums,int k) { unordered_map<int,int> mp; mp[0] = 1; int sum=0,count=0; for(int num:nums){ sum+=num; if(mp.find(sum-k)!=mp.end()) count+=mp[sum-k]; mp[sum]++; } return count; }
|
上面的代码思路解释,首先,先计算前缀和的值,然后加到map里面,之后看有没有出现过sum-k这种前缀和,如果有,那么这一段的和就是k了,且有$mp[sum-k]$个,这样就计算出了答案
例如:K-大师和他的领域
这一题要我们找存在几个区间满足既含有k又满足k是这个区间的中位数。
那么步骤:
1.令小于k的为-1,大于k的为1,等于k的为0
2.计算前缀和,统计前缀和一样的区间,这样区间内的和为0,说明k是这个区间的中位数,再使用$\Sigma{i=1}^{map.size()}C{m_i}^2$计算总数就行了,这样得到可能的区间。
3.但是还要保证区间内包含k,所以找到k位置,在两个k间的需要再用相同方法计算一次,
4.最后减去即可
就是说,要统计合法区间时,都可以把题目的要求变形一下,然后就可以使用这个思路了
4. 前缀和与差分数组
差分数组用于区间修改,单点查询:
这一个没什么好说的,就是差分和前缀的互逆关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| vector<int> diff(n+2, 0); diff[1] = a[1]; for (int i = 2; i <= n; i++) { diff[i] = a[i] - a[i-1]; }
void rangeAdd(int l, int r, int val) { diff[l] += val; diff[r+1] -= val; }
for (int i = 1; i <= n; i++) { a[i] = a[i-1] + diff[i]; }
|
5. 前缀最大/最小值
1 2 3 4 5 6 7 8 9 10
| vector<int> preMax(n+1, INT_MIN); for (int i = 1; i <= n; i++) { preMax[i] = max(preMax[i-1], a[i]); }
vector<int> sufMin(n+2, INT_MAX); for (int i = n; i >= 1; i--) { sufMin[i] = min(sufMin[i+1], a[i]); }
|
6. 前缀异或和
用于处理区间异或问题:
1 2 3 4 5 6
| vector<int> xorPre(n+1, 0); for (int i = 1; i <= n; i++) { xorPre[i] = xorPre[i-1] ^ a[i]; }
int xorsum = xorPre[r] ^ xorPre[l-1];
|
7.二维差分数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| vector<vector<int>> diff(m+2, vector<int>(n+2, 0));
void rangeAdd2D(int x1, int y1, int x2, int y2, int val) { diff[x1][y1] += val; diff[x2+1][y1] -= val; diff[x1][y2+1] -= val; diff[x2+1][y2+1] += val; }
vector<vector<int>> res(m+1, vector<int>(n+1, 0)); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { res[i][j] = res[i-1][j] + res[i][j-1] - res[i-1][j-1] + diff[i][j]; } }
|
然后很重要的一点是,有时可能是假设前缀和数组,最后再反推原数组来进行构造。