思路

对于每个位置的宝石,分别维护The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_i++(同色前一宝石所在位置)和The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_#define_02(同色后一宝石所在位置)。
注意到The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_i++_03很小,那么对于每次询问,我们可以从开始点往后找,查询当前点是否有同色点位于前面且位于The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_线段树_04内,有的话比较大小,决定是否跳过。
对于区间单点维护操作,我们需要对The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_线段树_05The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_c++_06进行更新,更新的过程类似于单链表,每个颜色单独构成一条链:

  1. 删除原有元素(讨论:元素位于序列头部、尾部、中间)。
  2. 添加新元素(讨论头插、尾插、中插)
    为了更快的查找插入元素的位置,我们可以对每个颜色维护一个​​​std::set​​,然后通过二分查找找位置插入。

对于查询操作和求和操作,显然可以使用线段树进行加速。我们对宝石的值维护一个区间和线段树,对每个点的The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_线段树_05节点维护一个区间最大值线段树,当最大值为The 2020 ICPC Asia Macau Regional Contest J.Jewel Grab 线段树+双向链表模拟_算法_08时就表示区间内无重复颜色。如果查询到一段区间无重复颜色,显然可以直接选择整个区间。对于有重复颜色的区间,我们在线段树上二分查找有重复颜色节点的下一节点,并根据值的大小决定是否进行更新即可。

#include <bits/stdc++.h>
#define int long long

const int N = 2e5 + 10;

int c[N], v[N];
int pre[N], nxt[N], last[N], his[N];

namespace SegmentTree{
int tree[N << 2], maxx[N << 2];

#define lson rt << 1, l,
#define rson rt << 1 | 1, mid + 1,

inline void push_up(int rt){
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
maxx[rt] = std::max(maxx[rt << 1], maxx[rt << 1 | 1]);
}

void build(int rt, int l, int r){
if(l == r){
tree[rt] = v[l];
maxx[rt] = pre[l];
return;
}
int mid = l + r >> 1;
build(lson), build(rson);
push_up(rt);
}
//^single point update
inline void update(int rt, int l, int r, int pos){
if(l == r){
tree[rt] = v[pos];
maxx[rt] = pre[pos];
return;
}
int mid = l + r >> 1;
if(mid >= pos) update(lson, pos);
else update(rson, pos);
push_up(rt);
}
//^query the sum of a part
inline int query_sum(int rt, int l, int r, int L, int R){
//assert(L <= R);
if(l >= L && r <= R) return tree[rt];
int mid = l + r >> 1, ans = 0;
if(mid >= L) ans += query_sum(lson, L, R);
if(mid < R) ans += query_sum(rson, L, R);
return ans;
}
//^queyr the maxx previous position of the same color element
inline int find(int rt, int l, int r, int s, int p){
if(l == r) return l;
int mid = l + r >> 1, ans = -1;
if(maxx[rt << 1] >= s && mid >= p) ans = find(lson, s, p);
if(ans != -1) return ans;
if(maxx[rt << 1 | 1] >= s) ans = find(rson, s, p);
return ans;
}
}

namespace fastIO {
char buf[N], *s, *t;
inline int read(){
int f = 1, x = 0; char s = getchar();
while(s < '0'||s > '9'){ if(s == '-') f = -1; s = getchar(); }
while(s >= '0' && s <= '9'){ x = x * 10 + s - '0'; s = getchar();}
return x *= f;
}
}

namespace dbgt{
int serial = 0;
inline void out(){ std::cout << std::endl << "#DEBUG" << ++serial << ' '; }
}

std::set<int> g[N];


inline void solve(){
int n = fastIO::read(), m = fastIO::read();
//*PreWork_1: Get the position of the previous position of current position color
memset(last, -1, sizeof last);
for(register int i = 1; i <= n; i++){
c[i] = fastIO::read(), v[i] = fastIO::read();
pre[i] = last[c[i]];
last[c[i]] = i;
g[c[i]].emplace(i);
}
//*PreWork_2: Get the position of the next position of current postion color
memset(last, -1, sizeof last);
for(register int i = n; i >= 1; --i){
nxt[i] = last[c[i]];
last[c[i]] = i;
}
memset(last, -1, sizeof last);
for(register int i = 1; i <= n; i++) last[c[i]] = i;
// dbgt::out(); for(int i = 1; i <= n; i++) std::cout << pre[i] << ' ';
// dbgt::out(); for(int i = 1; i <= n; i++) std::cout << nxt[i] << ' ';
// dbgt::out(); for(int i = 1; i <= n; i++) std::cout << last[i] << ' ';
// memset(SegmentTree::maxx, 0, sizeof(SegmentTree::maxx));
// dbgt::out(); for(int i = 1; i <= (n << 2); i++) std::cout << SegmentTree::maxx[i] << ' ';
SegmentTree::build(1, 1, n);
//!Assume that the pre and nxt array has been deal done.

while(m--){
int op = fastIO::read();
if(op == 1){
int xx = fastIO::read(), cc = fastIO::read(), vv = fastIO::read();
//^Delete the element
g[c[xx]].erase(xx);
if(last[c[xx]] == xx) last[c[xx]] = pre[xx];
c[xx] = cc, v[xx] = vv;
if(nxt[xx] == -1){
if(pre[xx] != -1) nxt[pre[xx]] = -1;// pre[xx] = -1;
} else {
if(pre[xx] == -1) pre[nxt[xx]] = -1;
else nxt[pre[xx]] = nxt[xx], pre[nxt[xx]] = pre[xx];
SegmentTree::update(1, 1, n, nxt[xx]);
}
//*Insert the element
auto it = g[cc].lower_bound(xx);
if(it == g[cc].end()){
nxt[xx] = -1, pre[xx] = last[cc];
if(pre[xx] != -1) nxt[pre[xx]] = xx;
SegmentTree::update(1, 1, n, xx);
}
else{
if(pre[*it] != -1){
nxt[pre[*it]] = xx, pre[xx] = pre[*it];
SegmentTree::update(1, 1, n, xx);
} else {
pre[xx] = -1;
SegmentTree::update(1, 1, n, xx);
}
nxt[xx] = *it, pre[*it] = xx;
SegmentTree::update(1, 1, n, *it);
}
g[cc].emplace(xx);
last[cc] = std::max(last[cc], xx);
} else {
int s = fastIO::read(), t = fastIO::read();
//dbgt::out(); std::cout << s << ' ' << t << std::endl;
int ans = 0, pos = s;
std::vector<int> book;
for(int i = 0; i <= t && pos <= n; i++){
int fnd = SegmentTree::find(1, 1, n, s, pos);
//dbgt::out(); std::cout << s << ' ' << fnd << std::endl;
if(fnd == -1) { ans += SegmentTree::query_sum(1, 1, n, pos, n); break; }
book.emplace_back(c[fnd]);
if(i < t){
if(!his[c[fnd]]) his[c[fnd]] = v[pre[fnd]];
if(his[c[fnd]] < v[fnd]){
ans += (v[fnd] - his[c[fnd]]);
his[c[fnd]] = v[fnd];
}
}
//std::cout << "#DEBUG: " << pos << ' ' << fnd - 1 << std::endl;
ans += SegmentTree::query_sum(1, 1, n, pos, fnd - 1);
pos = fnd + 1;
}
std::cout << ans << '\n';
for(auto x : book) his[x] = 0;

}
}


}

signed main(){
solve();
return 0;
}