A Simple Problem with Integers题解

一道luogu没有的题

Description

给定一个数列(N(N≤10^5)),给出几次操作

  • ( ext{C l r d}),把数列中第(l)~(r)个数都加(d)

  • ( ext{Q l r}),询问数列中第(l)~(r)个数的和

Solve

显然这道题线段树秒切,但是手贱去写了树状数组。

用一种差分的思想,用(b)数组的前缀和(sum_{i=1}^xb[i])表示(a[x])增加的值。那么序列(a)的前缀和整体增加的值就是(sum_{i=1}^xsum_{j=1}^ib[j])

上式可以改写成:

[sum_{i=1}^xsum_{j=1}^ib[j]=sum_{i=1}^x(x-i+1) ast b[i]=(x+1)sum_{i=1}^x b[i]- sum_{i=1}^x i ast b[i] ]

于是我们就可以用两个树状数组,另一个维护(i ast b[i])的前缀和,和上面的方法类似。

在加操作时,我们需要

1.在树状数组(c_0)中,把位置(l)上的数加(d)

2.在树状数组(c_0)中,把位置(r+1)上的数减(d)

3.在树状数组(c_1)中,把位置(l)上的数加(last d)

4.在树状数组(c_1)中,把位置(r+1)上的数加((r+1) ast d)

统计答案就是把答案拆成(1)$r$和$1$(l-1)来处理

答案为((sum[r]+(r+1)ast ask(c_0,r)-ask(c_1,r))-(sum[l-1]+last ask(c_0,l-1)-ask(c_1,l-1)))

这种思想可以推广一下

Code

#include <bits/stdc++.h>
#define ll long long
#define MAXN 100100

using namespace std;

int n, q;
ll A[MAXN], B[2][MAXN];// B[0]为修改的累加, B[1]为i*修改的累加 

void add(int k, int x, ll y)
{
	for (; x <= n; x += x&-x)
	{
		B[k][x] += y;
	}
}

ll ask(int k, int x)
{
	ll ans = 0;
	for (; x; x -= x&-x)
	{
		ans += B[k][x];
	}
	return ans;
}

int main()
{
	scanf("%d%d", &n, &q);
	int a;
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a);
		A[i] = A[i-1]+a;
	}
	getchar();
	char tmp;
	int r, l, d;
	for (int i = 0; i < q; i++)
	{
		scanf("%c", &tmp);
		if (tmp == 'C')
		{
			scanf("%d%d%d", &l, &r, &d);
			add(0, l, d);
			add(0, r+1, -d);
			add(1, l, (ll)l*d);
			add(1, r+1, (ll)-(r+1)*d);
		}
		else if (tmp == 'Q')
		{
			scanf("%d%d", &l, &r);
			printf("%lld
", (ll)(A[r]+(r+1)*ask(0, r)-ask(1, r))
								- (A[l-1]+l*ask(0,l-1)-ask(1, l-1)));
		}
		getchar();
	}
	return 0;
 } 
原文地址:https://www.cnblogs.com/martian148/p/13878652.html