SDFZOJ 2810 序列更新

Problem

题目传送门

Solution

看到题目中有这么一段话:

对于一个长度为 \(n\) 的 \(a\) 序列,有两次操作:

  1. 0 L R x b,表示对于 \(i∈[L,R]\),\(a_i\) 加上 \(x-(i-L)b\)
  2. 1 L R, 输出 \(\large{\sum_{i=L}^R a_i}\)。

明显 \(-(i-L)b\) 形成了等差数列。立即想到二阶差分。假设一开始所有数均为 \(0\),修改后为:

\(l+1\) \(l+2\) \(\cdots\) \(r\) \(r+1\) \(r+2\)
第零阶 \(-b\) \(-2b\) \(\cdots\) \((l-r)b\) \(0\) \(0\)
第一阶 \(-b\) \(-b\) \(\cdots\) \(-b\) \((r-l)b\) \(0\)
第二阶 \(-b\) \(0\) \(\cdots\) \(0\) \((r-l+1)b\) \((l-r)b\)

然后就可以从第二阶直接单点修改,利用线段树维护区间 \([l, r]\) 的二阶前缀和的和即可。

这是个基本的 trick。


那怎么求区间 \([l, r]\) 的二阶前缀和的和呢?先明确一些常量:

\(mid=\dfrac{l+r}{2}\)

\(f_{l, r}\) 为区间 \([l, r]\) 的一阶前缀和的和。

\(g_{l, r}\) 为区间 \([l, r]\) 的二阶前缀和的和。


举个例子,假设其第 \(0\) 阶为 \(1,2,3,4\)。

那么将 \([1,2]\) 和 \([3,4]\) 两个区间合并为 \([1,4]\)。

那么结果为:

\([1 \times 1, 1 \times 2 + 2 \times 1, 1 \times 3 + 2 \times 2 + 3 \times 1, 1 \times 4 + 2 \times 3 + 3 \times 2 + 4 \times 1]\)

我们拿出来 \(2\) 的贡献:

\([2 \times 0, 2 \times 1, 2 \times 2, 2 \times 3]\)

那么左边的部分的和明显是 \(g_{1, 2}\) 中 \(2\) 的贡献。

右边的是一个等差数列,贡献为 \(len_l \times f_{1, 2} + \frac{(1 + len_r) \times len_r}{2} \times f_{1, 2}\)。

而右边的 \([3, 4]\) 贡献明显是 \(g_{3, 4}\),于是二阶差分的式子是:

\[len_l = mid - l + 1 \]

\[len_r = r - mid \]

\[g_{l, r} = g_{l, mid} + g_{mid + 1, r} + len_l \times f_{l, r} + \frac{(1 + len_r) \times len_r}{2} \times f_{l, r} \]

这题就基本上做完了,剩下的是线段树的基本操作。

AC Code

# include <cstdio>

# define maxn 500000
# define mod 1000000007
long long n, q, op, l, r, x, y, ans, c[maxn], tag[maxn], f[maxn], g[maxn], s[maxn];

void calc(long long &x) {
	x = (x % mod + mod) % mod;
}

void push_up(long long root) {
	c[root] = c[root * 2] + c[root * 2 + 1];
    calc(c[root]);
}

void push_down(long long l, long long r, long long root) {
    long long mid = (l + r) / 2;
	tag[root * 2] += tag[root];
    calc(tag[root * 2]);
	tag[root * 2 + 1] += tag[root];
	calc(tag[root * 2 + 1]);
    c[root * 2] += (mid - l + 1) * tag[root];
	calc(c[root * 2]);
    c[root * 2 + 1] += (r - mid) * tag[root];
	calc(c[root * 2 + 1]);
    tag[root] = 0;
}

void push_up2(long long l, long long r, long long root) {
	int mid = (l + r) / 2;
	s[root] = s[root * 2] + s[root * 2 + 1];
    calc(s[root]);
	f[root] = f[root * 2] + f[root * 2 + 1] + (r - mid) * s[root * 2];
	calc(f[root]);
	g[root] = g[root * 2] + g[root * 2 + 1] + (r - mid) * f[root * 2] + (r - mid) * (r - mid + 1) / 2 * s[root * 2];
	calc(g[root]);
}

void update(long long l, long long r, long long ll, long long rr, long long x, long long root) {
	if(ll <= l && r <= rr) {
		c[root] += (r - l + 1) * x;
        tag[root] += x;
		calc(c[root]);
		calc(tag[root]);
		return;
	}
	long long mid = (l + r) / 2;
	push_down(l, r, root);
	if(ll <= mid)
		update(l, mid, ll, rr, x, root * 2);
	if(mid < rr)
		update(mid + 1, r, ll, rr, x, root * 2 + 1);
	push_up(root);
}

void update2(long long l, long long r, long long x, long long y, long long root) {
	if(x < l || x > r)
		return;
	if(l == r) {
        s[root] += y;
        f[root] += y;
		g[root] += y;
		calc(s[root]);
		calc(f[root]);
		calc(g[root]);
		return;
	}
	long long mid = (l + r) / 2;
	if(x <= mid)
		update2(l, mid, x, y, root * 2);
	if(mid < x)
		update2(mid + 1, r, x, y, root * 2 + 1);
	push_up2(l, r, root);
}

long long query(long long l, long long r, long long ll, long long rr, long long root) {
	if(ll <= l && r <= rr)
		return c[root];
	long long mid = (l + r) / 2, res = 0;
	push_down(l, r, root);
	if(ll <= mid)
		res += query(l, mid, ll, rr, root * 2);
	if(mid < rr)
		res += query(mid + 1, r, ll, rr, root * 2 + 1);
	calc(res);
	return res;
}

long long query2(long long l, long long r, long long ll, long long rr, long long root) {
	if(ll <= l && r <= rr) {
		long long res = g[root] + (rr - r) * f[root] + (rr - r) * (rr - r + 1) / 2 * s[root];
		calc(res);
		return res;
	}
	long long mid = (l + r) / 2, res = 0;
	if(ll <= mid)
		res += query2(l, mid, ll, rr, root * 2);
	if(mid < rr)
		res += query2(mid + 1, r, ll, rr, root * 2 + 1);
	calc(res);
	return res;
}

int main( ) {
	
	scanf("%lld %lld", &n, &q);
		
	for(long long i = 1; i <= n; i++) {
		scanf("%lld", &x);
		update(1, n, i, i, x, 1);
	}
	
	while(q --) {
		scanf("%lld", &op);
		if(op == 0) {
			scanf("%lld %lld %lld %lld", &l, &r, &x, &y);
			update(1, n, l, r, x, 1);
			update2(1, n, l + 1, -y, 1);
			update2(1, n, r + 1, (r - l + 1) * y, 1);
			update2(1, n, r + 2, (l - r) * y, 1);
		}
		else if(op == 1) {
			scanf("%lld %lld", &l, &r);
			ans = query(1, n, l, r, 1) + query2(1, n, 1, r, 1);
			if(l > 1)	ans -= query2(1, n, 1, l - 1, 1);
			calc(ans);
			printf("%lld\n", ans);
		}
	}
	
	return 0;
	
}
posted @ 2026-04-19 16:58  Zdy2075  阅读(23)  评论(1)    收藏  举报