23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并

个人Limitの线段树题单题解主目录:

给出一个数列23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_数据结构,有23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_权值_02个操作,每种操作是把区间23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_03中等于23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_线段树_04的数改成23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_05.输出23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_权值_02步操作完的数列。

洛谷传送门:​​CF911G Mass Change Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)​

CF传送门:​​G. Mass Change Queries (codeforces.com)​

题目分析

容易发现数列的范围极小,那么考虑开23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_权值_07棵[动态开点]线段树。

考虑区间修改,可以转化为线段树合并,即将权值对应权值线段树上的区间内点全部并到指定的线段树上。

由于单点具有互斥性,在每个点最多可以存在一个一个数字,因此不会出现合并叶子节点的情况,于是全部加和合并即可。

我们可以发现,叶节点只会存在两种情况:23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_数据结构_0823.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_0923.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_09表示叶子节点对应的权值存在:

23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_算法_11

如上图所示(实线表示动态开点开出来的点,虚心的表示实际不存在的节点(方便理解画上的)),序列长度为23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_12,则权值线段树值域也是23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_12,权值为23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_09对应的第一颗线段树也节点存在则表示该权值在原序列的23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_#define_15位置出现过,第二棵则表示权值为23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_线段树_16的在23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并_权值_17出现过…以此类推。

我们发现,由于动态开点的性质,只有实际到达过的节点到根节点之间的链才会实际存在,也就是说权值所对应的权值线段树只会保存改权值在序列出现过的位置的信息,不存在的位置线段树上也不会存在对应的节点。那么我们可以不维护权值线段树的具体值,仅仅维护动态开点的过程,合并的时候直接把两棵权值线段树合并。而查询的时候,只要某叶节点存在则一定表示该权值在该位置出现过。

Code

#include <bits/stdc++.h>
#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,
#define rson rs, id, mid + 1,

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;
}