个人Limitの线段树题单题解主目录:Limitの线段树题单 题解目录_HeartFireY的博客-CSDN博客
给出一个数列 a i ∈ [ 1 , 100 ] a_i \in [1, 100] ai∈[1,100],有 q q q个操作,每种操作是把区间 [ l , r ] [l,r] [l,r]中等于 x x x的数改成 y y y.输出 q q q步操作完的数列。
洛谷传送门:CF911G Mass Change Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
容易发现数列的范围极小,那么考虑开 100 100 100棵[动态开点]线段树。
考虑区间修改,可以转化为线段树合并,即将权值对应权值线段树上的区间内点全部并到指定的线段树上。
由于单点具有互斥性,在每个点最多可以存在一个一个数字,因此不会出现合并叶子节点的情况,于是全部加和合并即可。
我们可以发现,叶节点只会存在两种情况: 0 0 0或 1 1 1, 1 1 1表示叶子节点对应的权值存在:

如上图所示(实线表示动态开点开出来的点,虚心的表示实际不存在的节点(方便理解画上的)),序列长度为 4 4 4,则权值线段树值域也是 4 4 4,权值为 1 1 1对应的第一颗线段树也节点存在则表示该权值在原序列的 p o s = 1 pos=1 pos=1位置出现过,第二棵则表示权值为 2 2 2的在 p o s = 2 , 3 pos=2,3 pos=2,3出现过…以此类推。
我们发现,由于动态开点的性质,只有实际到达过的节点到根节点之间的链才会实际存在,也就是说权值所对应的权值线段树只会保存改权值在序列出现过的位置的信息,不存在的位置线段树上也不会存在对应的节点。那么我们可以不维护权值线段树的具体值,仅仅维护动态开点的过程,合并的时候直接把两棵权值线段树合并。而查询的时候,只要某叶节点存在则一定表示该权值在该位置出现过。
#include
#pragma gcc optimize("O2")
#pragma g++ optimize("O2")
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e7 + 10;
int a[N], root[N], ans[N];
namespace SegTree{
#define ls lc[rt]
#define rs rc[rt]
#define lson ls, id, l, mid
#define rson rs, id, mid + 1, r
int lc[N], rc[N], tot = 0;
void update(int &rt, int id, int l, int r, int pos){
if(!rt) rt = ++tot;
if(l == r) return;
int mid = l + r >> 1;
if(mid >= pos) update(lson, pos);
else update(rson, pos);
}
int merge(int u, int v){
if(!u || !v) return u + v;
lc[u] = merge(lc[u], lc[v]);
rc[u] = merge(rc[u], rc[v]);
return u;
}
void modify(int &rt, int id, int l, int r, int L, int R, int &mod){
if(!rt) return;
if(l >= L && r <= R){
mod = merge(rt, mod);
rt = 0;
return;
}
if(!mod) mod = ++tot;
int mid = l + r >> 1;
if(mid >= L) modify(lson, L, R, lc[mod]);
if(mid < R) modify(rson, L, R, rc[mod]);
}
void getans(int rt, int id, int l, int r){
if(!rt) return;
if(l == r) return (void)(ans[l] = id);
int mid = l + r >> 1;
getans(lson), getans(rson);
}
}
inline void solve(){
int n = 0; cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) SegTree::update(root[a[i]], a[i], 1, n, i);
int q = 0; cin >> q;
for(int i = 1; i <= q; i++){
int l, r, x, y; cin >> l >> r >> x >> y;
if(x == y) continue;
SegTree::modify(root[x], x, 1, n, l ,r, root[y]);
}
for(int i = 1; i <= 100; i++){
if(root[i]) SegTree::getans(root[i], i, 1, n);
}
for(int i = 1; i <= n; i++) cout << ans[i] << " \n"[i == n];
}
signed main(){
ios_base::sync_with_stdio(false), cin.tie(0);
solve();
return 0;
}