差分前缀和
前缀和操作
如果我给你一串长度为n的数列a1,a2,a3…an,再给出m个询问,每次询问给出L,R两个数,要求给出区间[L,R]里的数的和,你会怎么做,若是没有了解过前缀和的人看到这道题的想法可能是对于m次询问,我每次都遍历一遍它给的区间,计算出答案,这样子的方法固然没错,但是用了两个循环,其时间复杂度达到了O(n*n),如果数据量稍微大一点就有可能超时,而我们如果使用前缀和的方法来做的话就能够将时间复杂度降到O(n+m),大大节省了运算时间。
int tr[1005] = {0};
for(int i = 1; i <= n; i++)
tr[i] += tr[i - 1];
然后你需要哪个区间的数的和,就可以用tr[R] - tr[L - 1]
差分
给你一串长度为n的数列a1,a2,a3…an,要求对a[L]~a[R]进行m次操作:
操作一:将a[L]~a[R]内的元素都加上P
操作二:将a[L]~a[R]内的元素都减去P
最后再给出一个询问求a[L]-a[R]内的元素之和?
你会怎么做呢?你可能会想,我对于m次操作每次都遍历一遍a[L]~a[R],给区间里的数都加上P或减去P,最后再求一次前缀和就行了。没错,这样子确实也能得出正确答案,但时间复杂度却高达O(M*n+q),对于1<=n,m<=1e5这个数据范围来说直接就tle了,所以说这个方法不可行。这个时候差分就派上了用场,我们可以用另一个差分数组br[]来储存每一步操作,比如对[a,b]的元素同时加c,那么我们可以让br[a]+=c,br[b + 1]-=c,这样的话求一遍前缀和就能让区间内的所有数都加c
#include<bits/stdc++.h>
using namespace std;
int main()
{
int tr[10005] = {0};
int br[10005] = {0};//差分数组
int n, m, a, b, c;
int add = 0;
cin>>n>>m;//n是数组个数,m是进行的操作次数
for(int i = 1; i <= n; i++)
{
cin>>tr[i];
}
for(int i = 1; i <= m; i++)
{
cin>>a>>b>>c;//对[a,b]的元素进行加c
br[a]+=c;//差分数组
br[b + 1]-=c;
}
for(int i = 1; i <= n; i++)
{
add+=br[i];//add是一直在相加的
tr[i] += tr[i - 1] + add;//求前缀和,同时加上add,即加上该位置的元素经过操作后加的数
}
cin>>a>>b;//输入所需输出的区间
cout<<tr[b] - tr[a - 1];
//cout<<ans;
}
例题
题目描述
牛牛现在在花园养了n棵树,按顺序从第1棵到第n棵排列着。牛牛每天会按照心情给其中某一个区间的树浇水。例如如果某一天浇水的区间为[2,4],就是牛牛在这一天会给第2棵,第3棵和第4棵树浇水。树被浇水后就会成长,为了简化问题,我们假设在初始时所有树的高度为0cm。每过去一天树会自然成长1cm,每次树被浇水后当天会额外成长1cm。m天中牛牛每天都都会选一个区间[l,r]对这个区间内的树进行浇水,牛牛想知道m天后有多少棵树的高度为奇数,你能告诉牛牛吗?
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回m天后高度为奇数的树的数量
* @param n int整型
* @param m int整型
* @param l int整型vector
* @param r int整型vector
* @return int整型
*/
int oddnumber(int n, int m, vector<int>& l, vector<int>& r) {
// write code here
int sum[200020] = {0};
int ans = 0;
for(int i = 0; i < m; i++)
{
sum[l[i]]++;
sum[r[i] + 1]--;
}
for(int i = 1; i <= n; i++)
{
sum[i] += sum[i - 1];
}
for(int i = 1; i <= n; i++)
{
if((sum[i] + m) % 2 == 1)
ans++;
}
return ans;
}
};