• 前缀和+差分+ 树状数组


    前缀和+差分的理论知识

    前缀和相关题:

    「USACO16JAN」子共七 Subsequences Summing to Sevens

    定理:若两个数相减 mod k == 0,那么这两个数mod k 的余数相同。(看别人的题解得到的)

    #include
    using namespace std;
    
    const int N = 5e4;
    
    int n;
    int a[N + 1];//前n项和mod 7 
    int bgin[7],tail[7];  //bgin存第1个mod为i的数据编号,tail存最后一个mod为i的数据编号 
    
    int main()
    {
      // 若两个数相减mod 7==0,那么这俩个数mod 7的余数一定相同 
    	ios::sync_with_stdio(false);
    	
    	int res = 0;//存结果 
    	cin >> n ;
    	for(int i = 1; i <= n; i ++)
    	{
    		cin >> a[i];
    		a[i] += a[i-1];
    		a[i] %= 7;
    	}
    	for(int i = 0; i <= n; i ++)  //从前往后搜,且这里必须是i=0开始,不能从i=1开始,考虑数据1 3 4即明白 
    		tail[a[i]] = i;
    	for(int i = n; i >= 0; i --) //从后往前搜, 并且这里必须要包含0也即i>=0 
    		bgin[a[i]] = i;
    	
    	for(int i = 0; i < 7; i ++)
    		res = max(res, tail[i] - bgin[i]);
    
    	cout << res << endl;
    	return 0;
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    「USACO05JAN」Moo Volume S

    公式推到:(假设数据有序)

    第i个位置的牛与前i-1个牛聊天音量的总和:
    x i − x i − 1 + x i − x i − 2 + ⋯ + x i − x 2 + x i − x 1 = ( i − 1 ) ⋅ ∑ k = 1 i − 1 x k x_i - x_{i-1} + x_i - x_{i-2} +\cdots+ x_i -x_2+x_i - x_1 = (i-1)\cdot\sum\limits_{k=1}^{i-1}x_k xixi1+xixi2++xix2+xix1=(i1)k=1i1xk
    下式res结果要乘2 是因为只计算了 ( x i > x j ) 时 x i − x j (x_i>x_j)时x_i-x_j (xi>xj)xixj并没有计算 x j − x i x_j-x_i xjxi,且 ∣ x i − x j ∣ = ∣ x j − x i ∣ \lvert x_i-x_j\rvert=\lvert x_j-x_i\rvert xixj=xjxi

    r e s = 2 ⋅ ∑ i = 1 n ( i − 1 ) ⋅ ∑ k = 1 i − 1 x k res =2\cdot \sum\limits_{i=1}^{n}(i-1)\cdot\sum\limits_{k=1}^{i-1}x_k res=2i=1n(i1)k=1i1xk

    #include
    using namespace std;
    const int N = 1e5 + 1;
    typedef long long ll;
    
    ll n;
    ll a[N]; //存放数字 
    ll sum[N]; //前n项和 
    int main()
    {
    	ios::sync_with_stdio(false);
    	
    	cin >> n;
    	ll res = 0;//结果 
    	for(int i = 1; i <= n; i ++)
    		cin >> a[i];
    	//排序 
    	sort(a+1,a+n+1);
    	//求前n项和 
    	for(int i = 1; i <= n; i ++)
    		sum[i] = sum[i-1] + a[i];
    	// 求最终结果	
    	for(int i = 1; i <= n; i ++)
    		res += (i - 1)*a[i] - sum[i-1];//此公式需要推到 
    	cout << res * 2 << endl;
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    二维前缀和:

    洛谷 P1387 最大正方形

    #include
    using namespace std;
    const int N = 100 + 3; 
    int n, m;
    
    int matrix[N][N];
    
    int sum[N][N]; //前缀和 
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	int res = 0;
    	cin >> n >> m;
    	for(int i = 1; i <= n; i ++)
    		for(int j = 1; j <= m; j ++)
    		{
    			cin >> matrix[i][j];
    			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i][j];
    		}
    	for(int len = 1; len <= min(n, m); len ++)//边长 
    		for(int i = len; i <= n; i ++)
    			for(int j = len; j <= m; j ++)
    			{
    				if((sum[i][j] - sum[i - len][j] - sum[i][j - len] + sum[i - len][j - len]) == (len * len))
    					res = max(res, len);
    			}
    	cout << res << endl;
    	
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    「HNOI2003」激光炸弹

    以下都是AC代码,为了防止MLE进行了空间优化,也即sum即存储原始数据,也存储前缀和。

    #include
    using namespace std;
    
    const int N = 5e3 + 3;
    
    int n, m;
    
    
    int sum[N][N];//前缀和 
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	int res = 0, mx = 0, my = 0;
    	cin >> n >> m;
    	for(int i = 0; i < n; i ++)
    	{
    		int x, y, v;
    		cin >> x >> y >> v;
    		sum[x + 1][y + 1] = v;
    		mx = max({mx, x + 1, m});
    		my = max({my, y + 1, m});//必须把m算进去,考虑此类情况:n=1 m= 2 ,第二行输入 0,0 ,1 
    	}
    	for(int i = 1; i <= mx; i ++)
    		for(int j = 1; j <= my; j ++)
    			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];
    	for (int i = m; i <= mx; i ++)
    		for(int j = m; j <= my; j ++)
    			res = max(res, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);		
    	cout << res << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    #include
    using namespace std;
    
    const int N = 5000 + 11;
    
    int n, m;
    
    int sum[N][N];//前缀和 
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	int res = 0, mx = 0, my = 0;
    	cin >> n >> m;
    	for(int i = 0; i < n; i ++)
    	{
    		int x, y, v;
    		cin >> x >> y >> v;
    		sum[x + 1][y + 1] = v;//这个是让坐标从1开始,而不是从0开始 
    		mx = max(mx, x + 1);
    		my = max(my, y + 1);
    	}
    	for(int i = 1; i <= min(mx + m, 5001); i ++) //这里i不能小于等于N,因为你数组就开N个, 当然如果这里为了省事,可直接i<=5001 下面一样 
    		for(int j = 1; j <= min(my + m, 5001); j ++)
    			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];//这里其实进行了空间的优化 
    	for (int i = m; i <= min(mx + m, 5001); i ++)
    		for(int j = m; j <= min(my + m, 5001); j ++)
    			res = max(res, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);		
    	cout << res << endl;
    	return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    CPS-邻域均值

    #include
    using namespace std;
    const int N = 600 + 3;
    int n, l, r, t;
    
    int  sum[N][N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	int res = 0;
    	
    	cin >> n >> l >> r >> t;
    	for(int i = 1; i <= n; i ++)
    		for(int j = 1; j <= n; j ++)
    		{
    			cin >> sum[i][j];
    			sum[i][j] = sum[i-1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j]; 
    		}
    	
    	// 中心点为参考
    	for(int i = 1; i <= n; i ++)
    		for(int j = 1; j <= n; j ++)
    		{
    			int loc_l = max(1, j - r);
    			int loc_r = min(n, j + r);//不能直接把变量设为r,t,会与前面的变量名重复。 
    			int loc_t = max(1, i - r);
    			int loc_b = min(n, i + r);
    			double counts = (loc_r - loc_l + 1) * (loc_b - loc_t + 1); //个数 
    			if( ((sum[loc_b][loc_r] - sum[loc_b][loc_l - 1] - sum[loc_t - 1][loc_r] + sum[loc_t - 1][loc_l - 1]) / counts) <= t)
    				res += 1;
    		}
    	cout << res << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    csp- 期末预测之最佳阈值

    #include
    using namespace std;
    
    const int M = 1e5 + 1;
    
    int m;
    
    struct Node
    {
    	int y,result;
    	bool operator < (const struct Node n)//根据y排序 
    	{
    		if (y < n.y)
    			return true;
    		return false;
    	}
    }node[M];
    
    int sum[M]; 
    
    int main()
    {
    	
    	ios::sync_with_stdio(false);
    	
    	int res = 0, maxTotal = 0;//res:最大阈值,maxTotal: 预测正确次数最大 
    	cin >> m;
    	
    	for(int i = 1; i <= m; i ++)
    		cin >> node[i].y >> node[i].result;
    
    	sort(node + 1, node + m + 1);
    	
    	
    	for(int i = 1; i <= m; i ++)
    		sum[i] = sum[i-1] + node[i].result;
    	
    	for(int i = 1; i <= m; i ++)
    	{
    		if(node[i].y == node[i-1].y) //这一步是必须的,如果y连续出现相同的数,只需计算第一个,后面的不要再计算 ,因为下面的sum[i-1]会出错 
    			continue;
    		int total = (i - 1 - sum[i-1]) + (sum[m] - sum[i - 1]);//计算预测正确次数, +前部分是 小于y的预测正确的次数,+ 后半部分是 大于y预测正确的次数 
    		
    		//找最大的maxtotal和阈值 
    		if(maxTotal <= total)
    		{
    			maxTotal = total;
    			res = node[i].y;
    		}
    		
    	}
    	cout << res << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    差分相关题:

    a i + 1 = b i + 1 − b i a_{i+1} = b_{i+1} - b_i ai+1=bi+1bi其中 a a a是差分矩阵, b b b是原矩阵,对差分矩阵求前缀和即是原矩阵。如果对原矩阵 [ l , r ] [l,r] [l,r]增加k,那么可以对差分矩阵 a [ l ] + = k , a [ r + 1 ] − = k a[l] += k, a[r + 1] -= k a[l]+=k,a[r+1]=k。可以减少时间复杂度。
    ∑ i = 1 n a i = ∑ i = 1 n ∑ j = 1 i b j = ∑ i = 1 n ( n − i + 1 ) b i = ( n + 1 ) ∑ i = 1 n b i − ∑ i = 1 n i ⋅ b i \sum\limits_{i=1}^n a_i = \sum\limits_{i=1}^n \sum\limits_{j=1}^i b_j = \sum\limits_{i=1}^n (n - i + 1) b_i = (n + 1)\sum\limits_{i =1}^n b_i-\sum\limits_{i = 1}^n i\cdot b_i i=1nai=i=1nj=1ibj=i=1n(ni+1)bi=(n+1)i=1nbii=1nibi
    有关 ∑ i = 1 n ∑ j = 1 i b j = ∑ i = 1 n ( n − i + 1 ) b i \sum\limits_{i=1}^ n \sum\limits_{j=1}^ i b_j = \sum\limits_{i=1}^n (n - i + 1) b_i i=1nj=1ibj=i=1n(ni+1)bi 这一步的产生,考虑有n个 b 1 b_1 b1,n-1个 b 2 b_2 b2 ,以此类推有 n − i + 1 n-i+1 ni+1 b i b_i bi故得出结果。

    csp-出行计划

    q + k ≤ t i ≤ q + k + c i − 1 q + k \le t_i \le q +k+c_i -1 q+ktiq+k+ci1 t i + 1 − c i − k ≤ q ≤ t i − k t_i + 1- c_i - k \leq q \leq t_i - k ti+1cikqtik,也即做核酸的时间点要在 [ t i + 1 − c i − k , t i − k ] [t_i + 1- c_i - k , t_i - k] [ti+1cik,tik]内,对每个出行计划其范围内的时间点+1,时间点为整数,最终如果求q时刻满足出行的计划,直接根据下标q即可得。

    #include
    using namespace std;
    
    const int N =  2e5 + 3;
    
    int n, m, k; 
    int diff[N]; // 差分矩阵 ,差分矩阵的原矩阵表示在每个时间点,如果这个时间做核酸可以出行,则这里的值+1 
    int q[N];
    int main()
    {
    	ios::sync_with_stdio(false);
    	
    	cin >> n >> m >> k; 
    	for(int i = 0; i < n; i ++)
    	{
    		int ti, ci;
    		cin >> ti >> ci;
    		diff[max(1,ti + 1 - ci - k)] ++;//这里要和 1进行比较,如果不加会出现错误。 
    		diff[max(1,ti - k + 1)] --;
    	}
    	for(int i = 1; i < N; i ++)
    		diff[i] = diff[i - 1] + diff[i];
    	for(int i = 0; i < m; i ++)
    		cin >> q[i];
    	for(int i = 0; i < m; i ++)
    		cout << diff[q[i]] << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    P3397 地毯此题是很经典的二维差分问题。有关二维差分参见此博客

    #include
    using namespace std;
    
    const int N = 1000 + 3;
    
    int n, m;
    
    int diff[N][N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	
    	cin >> n >> m;
    	for(int i = 0; i < m; i ++)
    	{
    		int lx, ly, rx, ry;
    		cin >> lx >> ly >> rx >> ry;
    		diff[lx][ly] ++;
    		diff[lx][ry + 1] --;
    		diff[rx + 1][ly] --;
    		diff[rx + 1][ry + 1] ++;
    	} 
    	//求前缀和
    	for(int i = 1; i <= n; i ++)
    		for(int j = 1; j <= n; j ++)
    		{
    			diff[i][j] = diff[i-1][j] + diff[i][j - 1] - diff[i - 1][j - 1] + diff[i][j];//这里是根据差分求原矩阵
    		}
    	//输出
    	for(int i = 1; i <= n; i ++)
    	{
    		for(int j = 1; j <= n; j ++)
    		{
    			cout << diff[i][j] << " ";
    		}
    		cout << endl;
    	}
    		
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    P4552 [Poetize6] IncDec Sequence

    题解实在是不知道该怎么说,洛谷有相关题解,第一个写的很好。

    #include
    using namespace std;
    
    typedef long long LL;
    const int N = 100000 + 3;
    
    LL n;
    
    LL  diff[N];//a是原矩阵,diff是差分矩阵 ,必须是LL,int会报错,2^31,需要开LL 
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	LL count_operator = 0, count_result = 0; 
    	cin >> n;
    	LL last = 0;
    	for(int i = 1; i <= n; i ++)
    	{
    		LL t;
    		cin >> t;
    		diff[i] = t - last;
    		last = t;
    	}
    	LL sum_p = 0,sum_n = 0;
    	for(int i = 2; i <= n; i ++)
    	{
    		if(diff[i] < 0)
    			sum_n += diff[i];
    		else if(diff[i] > 0)
    			sum_p += diff[i];
    	}
    	count_operator = abs(sum_p + sum_n) + min(sum_p, abs(sum_n));
    	count_result = abs(sum_p + sum_n) + 1;
    	
    	cout << count_operator << endl;
    	cout << count_result << endl; 
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    树状数组

    #130. 树状数组 1 :单点修改,区间查询

    /*
      单点修改,区间查询  2022.9.9
      计算机中,负数用补码表示 ,所以lowbit = x & -x 
    */ 
    
    #include
    using namespace std;
    typedef long long ll;
    const ll N = 1e6 + 3; 
    
    ll t[N]; //存储的原数组的树状数组。
    
    struct Node
    {
    	ll id, l, r;
    }node[N];
    
    ll n, q;
    
    //单点修改,对下标为i的原数组增加x 
    void add(ll x, ll k)
    {
    	for(;x <= n; x += x & -x)
    		t[x] += k;
    }
    // 求下标x的原数组前缀和 
    ll prefix(ll x) 
    {
    	ll ans = 0;
    	for(;x >= 1; x -= x & -x)
    		ans += t[x];
    	return ans; 
    }
    // 求区间原数组[l,r]的区间和 
    ll quary(ll l, ll r)
    {
    	return prefix(r) - prefix(l - 1);
    }
    
    int main()
    {
    	cin >> n >> q;
    	for(ll i = 1; i <= n; i ++)
    	{
    		ll temp;
    		cin >> temp;
    		add(i, temp);
    	}
    	for(ll i = 0; i < q; i ++)
    		cin >> node[i].id >> node[i].l >> node[i].r;
    	for(ll i = 0; i < q; i ++)
    	{
    		if(node[i].id == 1)
    			add(node[i].l, node[i].r);
    		else if(node[i].id == 2)
    			cout << quary(node[i].l, node[i].r) << endl;
    	}
    	return 0;
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    写成模板

    /*
      单点修改,区间查询  2022.9.9
      计算机中,负数用补码表示 ,所以lowbit = x & -x 
    */ 
    
    #include
    using namespace std;
    typedef long long ll;
    
    
    //写成模板 
    const ll N = 1e6 + 3; 
    struct BIT
    {
    	ll t[N], n; //t[n]存储的原数组的树状数组,n是元素组的元素个数。
    	//单点修改,对下标为i的原数组增加x 
    	inline void add(ll x, ll k)
    	{
    		for(;x <= n; x += x & -x)
    			t[x] += k;
    	}
    	// 求下标x的原数组前缀和 
    	inline ll prefix(ll x) 
    	{
    		ll ans = 0;
    		for(;x >= 1; x -= x & -x)
    			ans += t[x];
    		return ans; 
    	}
    	// 求区间原数组[l,r]的区间和 ,区间查询
    	inline ll quary(ll l, ll r)
    	{
    		return prefix(r) - prefix(l - 1);
    	}
    }; 
    
    struct BIT bit;
    struct Node
    {
    	ll id, l, r;
    }node[N];
    ll q;
    int main()
    {
    	ios::sync_with_stdio(false);	
    	cin >> bit.n >> q;
    	for(ll i = 1; i <= bit.n; i ++)
    	{
    		ll temp;
    		cin >> temp;
    		bit.add(i, temp);
    	}
    	for(ll i = 0; i < q; i ++)
    		cin >> node[i].id >> node[i].l >> node[i].r;
    	for(ll i = 0; i < q; i ++)
    	{
    		if(node[i].id == 1)
    			bit.add(node[i].l, node[i].r);
    		else if(node[i].id == 2)
    			cout << bit.quary(node[i].l, node[i].r) << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63


    #131. 树状数组 2 :区间修改,单点查询

    /*
    	time: 2022.9.9
    	区间修改,单点查询 
    */
    
    #include
    using namespace std;
    
    typedef long long ll;
    
    const ll N = 1e6 + 3;
    struct BIT
    {
    	ll n, t[N]; //t[N]存储的是原数组的差分数组的树状数组
    	
    	//单点修改 (这里的单点是指对差分数组中的一个元素修改,而非原数组) 
    	inline void add(ll x,ll k)
    	{
    		for(; x<= n; x += x & -x)
    			t[x] += k;
    	}
    	
    	//区间修改,对原数组的[l,k]+k(指的是对原数组进行区间修改)
    	inline void inter_add(ll l, ll r, ll k)
    	{
    		add(l, k);
    		add(r + 1, -k);
    	}
    	
    	//单点查询,(这里的单点查询,是对原数组求其某下标的值),其实是求差分数组的前缀和 
    	inline ll ask(ll x)
    	{
    		ll ans = 0;
    		for(;x >= 1; x -= x & -x)
    			ans += t[x];
    		return ans;
    	}
    };
    
    struct BIT bit;
    
    ll q;
    struct Qu
    {
    	ll id,l,r,x;
    }Q[N];
    
    int main()
    {
    	cin >> bit.n >> q;
    	ll last = 0; 
    	for(ll i = 1; i <= bit.n; i ++)
    	{
    		ll temp;
    		cin >> temp;
    		bit.add(i, temp - last); // 在进行构造过程中,不能 bit.add(i, temp);,而应该用差分数组 
    		last = temp;
    	}
    	
    	for(ll i = 1; i <= q; i ++)
    	{
    		cin >> Q[i].id;
    		if(Q[i].id == 1)
    			cin >> Q[i].l >> Q[i].r >> Q[i].x;
    		else if(Q[i].id == 2)
    			cin >> Q[i].l;
    	}
    	
    	for(ll i = 1; i <= q; i ++)
    	{
    		if(Q[i].id == 1)
    			bit.inter_add(Q[i].l, Q[i].r, Q[i].x);
    		else if(Q[i].id == 2)
    		{
    			ll res = bit.ask(Q[i].l);
    			cout << res << endl;
    		}
    	}
    	
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    #132. 树状数组 3 :区间修改,区间查询

    /*
    	time: 2022.9.9 
    	区间修改,区间查询; 
    */
    
    #include
    using namespace std;
    typedef long long ll;
    
    const ll N = 1e6 + 3;
    struct BIT
    {
    	ll t1[N], t2[N], n;//t1[N]是差分数组的树状数组,t2[N]是i*diff[i]的树状数组
    	
    	//t1单点修改(这里是对差分数组单点修改)
    	inline void add1(ll x, ll k)
    	{
    		for(; x <= n; x += x & -x)
    			t1[x] += k;
    	} 
    	//t2 单点修改(这里是对i*diffi单点修改) 
    	inline void add2(ll x, ll k)
    	{
    		for(; x <= n; x += x & -x)
    			t2[x] += k;
    	} 
    	
    	//求 diff从1到x的前缀和 
    	inline ll prefix1(ll x)
    	{
    		ll ans = 0;
    		for(; x >= 1; x -= x & -x)
    			ans += t1[x];
    		return ans;
    	}
    	//求i* diff的从1到x前缀和 
    	inline ll prefix2(ll x)
    	{
    		ll ans = 0;
    		for(; x >= 1; x -= x & -x)
    			ans += t2[x];
    		return ans;
    	}
    	//区间查询,求原数组[l,r]的区间和 
    	inline ll inter_sum(ll l, ll r)
    	{
    		return ((r+1)*prefix1(r) - prefix2(r)) - (l*prefix1(l - 1) - prefix2(l - 1));
    	}
    	
    	//区间修改,对元素组[l,r]+k 
    	inline void inter_add(ll l, ll r, ll k)
    	{
    		add1(l, k);
    		add1(r + 1, -k);
    		//同时需要对 i*diff这个数组进行修改
    		add2(l, l * k);
    		add2(r + 1, - (r + 1) * k); 
    	}
    };
     
    struct BIT bit;
    
    ll q;
    struct Node
    {
    	ll id, l, r, x;
    }Q[N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	
    	cin >> bit.n >> q;
    	
    	ll last = 0;
    	
    	for(ll i = 1;i <= bit.n; i ++)
    	{
    		ll temp;
    		cin >> temp;
    		bit.add1(i, temp - last);
    		bit.add2(i, i * (temp - last));
    		last = temp; 
    	}
    	for(ll i = 1; i <= q; i ++)
    	{
    		cin >> Q[i].id;
    		if(Q[i].id == 1)
    			cin >> Q[i].l >> Q[i].r >> Q[i].x;
    		else if(Q[i].id == 2)
    			cin >> Q[i].l >> Q[i].r;
    	}
    	
    	for(ll i = 1; i <= q; i ++)
    	{
    		if(Q[i].id == 1)
    			bit.inter_add(Q[i].l, Q[i].r, Q[i].x);
    		else if(Q[i].id == 2)
    			cout << bit.inter_sum(Q[i].l, Q[i].r) << endl;
    	}
    	return 0;
    }
    
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
  • 相关阅读:
    Tungsten Fabric Rabbitmq故障处理
    Apipost使用介绍
    【ASP.NET小白零基础入门】从0部署ASP.NET开发环境,并成功运行一个汉服图片管理系统
    LVS的实现NAT路由转发
    题目0164-数组合并
    Java_多态
    python一键采集高质量陪玩,心动主播随心选......
    c/c++ 静态代码检查工具
    LeetCode算法心得——美丽塔 I(HashMap)
    树莓派4B(64位)环境搭建
  • 原文地址:https://blog.csdn.net/qq_45670253/article/details/126747640