CF292E Copying Data 题解
分析
一眼可以看出,这题实际上是让你维护区间覆盖、单点查询操作。于是考虑线段树。
进一步观察可以发现,一次覆盖操作实际上是把 \(a_x\) 及其后的 \(k-1\) 个元素赋给 \(b_y\) 及其后的 \(k-1\) 个元素。我们画个图来理解一下:

也就是 \(b\) 数组里的每一个 \(b_y\) 在一次操作后都会变成 \(a\) 数组里的一个 \(a_{y+\Delta}\)。这个 \(\Delta\) 其实就是这次操作的 \(x - y\)。可以发现 \(\Delta\) 其实是 \(b\) 里某段区间的一个属性,代表这个区间里的每个 \(b_x\) 都即将被改成 \(a_{x+\Delta}\)。所以这玩意显然是可以用线段树维护的。把 \(\Delta\) 作为懒标记,每次将 \(\Delta\) 下传。查询时查到叶子就直接返回 \(a\) 里的对应值。
由于 \(x\) 和 \(y\) 可以相等,所以 \(\Delta\) 可以是 \(0\)。所以我们就不能用 \(0\) 标记区间没有被覆盖过,而要用无穷大来标记。
代码
#include <iostream>
using namespace std;
const int N = 131072;
const int inf = 2147483647;
int a[N << 2], b[N << 2];
int tag[N << 2];
void Build(int o, int l, int r) {
tag[o] = -inf; // 初始化懒标记:这里没有被覆盖过
if (l == r)
return;
int mid = (l + r) >> 1;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
}
void pushdown(int o) {
if (tag[o] == -inf)
return;
int t = tag[o];
tag[o] = -inf;
tag[o << 1] = tag[o << 1 | 1] = t; // 左右儿子都要移 △ 位
}
void Change(int o, int l, int r, int L, int R, int d) {
if (L <= l && r <= R) {
tag[o] = d; // 赋为 △
return;
}
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Change(o << 1, l, mid, L, R, d);
if (R > mid)
Change(o << 1 | 1, mid + 1, r, L, R, d);
}
int Query(int o, int l, int r, int x) {
if (l == r)
return (tag[o] == -inf ? b[x] : a[x + tag[o]]);
// 如果没有被覆盖过,则返回原本值。如果被覆盖过,返回 a 里的对应值。
pushdown(o);
int mid = (l + r) >> 1;
if (x <= mid)
return Query(o << 1, l, mid, x);
else
return Query(o << 1 | 1, mid + 1, r, x);
}
int main() {
Build(1, 1, N);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%d", a + i);
for (int i = 1; i <= n; i++) scanf("%d", b + i);
while (m--) {
int op, x, y, k;
cin >> op;
if (op == 1) {
cin >> x >> y >> k;
Change(1, 1, N, y, y + k - 1, x - y); // △ = x - y
} else {
cin >> x;
cout << Query(1, 1, N, x) << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号