SDFZOJ 2810 序列更新
Problem
Solution
看到题目中有这么一段话:
对于一个长度为 \(n\) 的 \(a\) 序列,有两次操作:
0 L R x b,表示对于 \(i∈[L,R]\),\(a_i\) 加上 \(x-(i-L)b\)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}\),于是二阶差分的式子是:
这题就基本上做完了,剩下的是线段树的基本操作。
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;
}

浙公网安备 33010602011771号