A. 三妖精SAY WA!!!
题目描述
即求出数组中 小于等于 上界 m m m 的最大三数之和。「YAC Round 1」三妖精SAY WA!!!
解法一 (暴力枚举)
由于数据范围较小,n ≤ 100 n \le 100 n ≤ 100 ,可以直接三重循环枚举三个数暴力解决这个问题,时间复杂度为 O ( n 3 ) O(n^3) O ( n 3 ) 。(注意三个数要求下标不重复)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int N = 110 ;int a[N], n, m, res;int main () { ios; cin >> n >> m; for (int i = 1 ; i <= n; i ++ ) cin >> a[i]; for (int i = 1 ; i <= n; i ++ ) for (int j = i + 1 ; j <= n; j ++ ) for (int k = j + 1 ; k <= n; k ++ ){ int sum = a[i] + a[j] + a[k]; if (sum <= m) res = max (res, sum); } cout << res << '\n' ; return 0 ; }
解法二 (枚举 + 二分)
我们需要在小于等于上界 m m m 的条件下,最大化三数之和的最大值,显然这个问题具有二段性,即 当三数之和 sum ≤ m \text{sum} \le m sum ≤ m 时,此时的和满足条件 “小于等于 m m m ”;当 sum > m \text{sum} > m sum > m ,此时的和则不满足。
故我们可以尝试用二分查找加以优化。先对数组进行排序,再枚举前两个数字,然后在区间 [ j + 1 , n ] [j + 1, n] [ j + 1 , n ] 范围内二分查找找到一个 a [ m i d ] a[mid] a [ mi d ] (m i d mid mi d 为第三个数下标)找到三数之和 a [ i ] + a [ j ] + a [ m i d ] a[i] + a[j] + a[mid] a [ i ] + a [ j ] + a [ mi d ] 在小于等于 m m m 条件下的最大值。这样就可以省去第三重循环,时间复杂度为 O ( n log n ) O(n \log n) O ( n log n ) .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int N = 110 ;int a[N], n, m, res;int main () { ios; cin >> n >> m; for (int i = 1 ; i <= n; i ++ ) cin >> a[i]; sort (a + 1 , a + n + 1 ); for (int i = 1 ; i <= n; i ++ ) for (int j = i + 1 ; j <= n; j ++ ){ int l = j + 1 , r = n; if (l > r) continue ; while (l < r){ int mid = l + r + 1 >> 1 ; if (a[i] + a[j] + a[mid] <= m) l = mid; else r = mid - 1 ; } int sum = a[i] + a[j] + a[l]; if (sum <= m) res = max (res, sum); } cout << res << '\n' ; return 0 ; }
B. 夜雀之歌
题目描述
构造一个长度为 n n n 的 01 01 01 字符串,满足 恰好 有 p p p 个不处于字符串两端的 0 0 0 两边是 1 1 1 ,且输出字典序最小的构造方案。「YAC Round 1」夜雀之歌
解题思路 (贪心)
需要满足 p p p 个字符 0 0 0 两边是 1 1 1 ,我们最少需要 p + 1 p + 1 p + 1 个字符 1 1 1 。也就是说,我们至少需要 2 × p + 1 2 \times p + 1 2 × p + 1 个字符可以进行构造,所以当 n < 2 × p + 1 n < 2 \times p + 1 n < 2 × p + 1 时,不可能完成构造,故直接输出 − 1 -1 − 1 。
对于 n ≥ 2 × p + 1 n \ge 2 \times p + 1 n ≥ 2 × p + 1 的情况,在满足 p p p 个 0 0 0 两边有 1 1 1 的情况下,为了使得字典序最小,我们只需要最少的 p + 1 p + 1 p + 1 个字符 1 1 1 ,并且尽可能地把 多余的 字符 0 0 0 放在前面即可(也就是构造成类似于 000 … 010101 000 \ldots 010101 000 … 010101 的形式。当然,也有可能没有多余的 0 0 0 )。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) int n, p; int main () { ios; int T = 1 ; cin >> T; while (T -- ){ cin >> n >> p; if (n < 2 * p + 1 ){ cout << -1 << '\n' ; continue ; } string res = "" ; for (int i = 2 * p + 1 ; i < n; i ++ ) res += "0" ; for (int i = 1 ; i <= p; i ++ ) res += "10" ; res += "1" ; cout << res << '\n' ; } return 0 ; }
C. BBA
题目描述
统计字符串中 “bba” “\text{bba}” “ bba ” 型 子序列 个数,字符串只包含小写英文字符。「YAC Round 1」BBA
解题思路 (前缀和)
这里给出一个比较好理解的方法。(也有更好的做法,欢迎讨论)
对于 “bba” “\text{bba}” “ bba ” 型子序列,只需要枚举每个位置的字符,然后观察前面与当前位置字符不同的每种字符各有多少个,然后累加一下组合数即可。
例如,枚举到当前位置 i i i 的字符为 c \text{c} c ,我们需要统计在位置 i i i 前面除字符 c \text{c} c 以外 a, b, d ... z \text{a, b, d ... z} a, b, d ... z 各有多少个,其中对于字符 q \text{q} q ,有 c n t cnt c n t 个,那么用当前位置 i i i 的字符 c \text{c} c 和前面的字符 q \text{q} q 构成 “bba” “\text{bba}” “ bba ” 型字符串的方案数就是 C c n t 2 C_{cnt}^2 C c n t 2 ,也就是 c n t × ( c n t − 1 ) 2 \frac{cnt \times (cnt - 1)}{2} 2 c n t × ( c n t − 1 ) 。
字符串只包含小写英文字符,所以我们只需要预处理每个字符的前缀和。c n t [ i ] [ j ] cnt[i][j] c n t [ i ] [ j ] 表示区间 [ 1 , i ] [1, i] [ 1 , i ] 内字符 ′ a ′ + j 'a' + j ′ a ′ + j 的总个数。
最后枚举每个位置,再枚举每个 与当前位置字符不同 的字符,累加答案即可。(注意开 long long \text{long long} long long )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedef long long ll;const int N = 1e5 + 10 ;int n; string s; ll cnt[N][26 ]; int main () { ios; cin >> n >> s; s = " " + s; for (int i = 1 ; i <= n; i ++ ) for (int j = 0 ; j < 26 ; j ++ ) cnt[i][j] = cnt[i - 1 ][j] + (s[i] - 'a' == j); ll res = 0 ; for (int i = 3 ; i <= n; i ++ ) for (int j = 0 ; j < 26 ; j ++ ) if (j != s[i] - 'a' ) res += cnt[i - 1 ][j] * (cnt[i - 1 ][j] - 1 ) / 2 ; cout << res << '\n' ; return 0 ; }
当然,我们可以发现累加答案时,只需要知道前面一个位置的数量,所以可以在累加答案的同时统计每个字符的个数,而且可以直接省去第一个维度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedef long long ll;int n; string s; ll cnt[26 ];int main () { ios; cin >> n >> s; s = " " + s; ll res = 0 ; for (int i = 1 ; i <= n; i ++ ){ for (int j = 0 ; j < 26 ; j ++ ) if (j != s[i] - 'a' ) res += cnt[j] * (cnt[j] - 1 ) / 2 ; cnt[s[i] - 'a' ] ++ ; } cout << res << '\n' ; return 0 ; }
D. 魔法使的事,能叫偷吗?
题目描述
在序列中找到一个包含数字 1 , 2 , 3 … , m 1, 2, 3 \ldots , m 1 , 2 , 3 … , m 的最小长度的连续区间 [ l , r ] [l, r] [ l , r ] 。若有多组解,输出 l l l 最小的那个。(数据保证一定有解)「YAC Round 1」魔法使的事,能叫偷吗?
解法一 (二分 + 滑动窗口)
显然,连续区间的长度 l e n len l e n 越长,包含的书的数量越多,更有可能包含所有种类的书。假设最终得到的区间的长度为 a n s ans an s ,当 l e n ≥ a n s len \ge ans l e n ≥ an s 时,序列中存在一个连续区间 [ l , r ] [l, r] [ l , r ] 包含了所有种类的书;当 l e n < a n s len < ans l e n < an s 时,序列中不存在区间 [ l , r ] [l, r] [ l , r ] 满足条件。可以看出该问题具有二段性。
所以,我们可以二分答案(区间长度),check \text{check} check 函数中,顺序枚举当前长度的起点 l l l 和终点 r r r ,如果当前区间 [ l , r ] [l, r] [ l , r ] 已经包含了所有种类的书,则直接返回 true \text{true} true ;如果枚举到最后都没有满足的区间,则返回 false \text{false} false 。
对于区间 [ l , r ] [l, r] [ l , r ] 内是否包含所有种类的书这个问题,可以用一个计数数组 c n t cnt c n t 和一个计数变量 d i f f diff d i ff 来实现一个 滑动窗口 算法。其中 c n t cnt c n t 数组统计的是 每个种类的书出现的次数 ,d i f f diff d i ff 统计的是 当前区间内不同种类的书的个数 。
先预处理初始情况下的滑动窗口,起点 l = 1 l = 1 l = 1 。对于当前种类为 c c c 的书,若 c n t [ c ] = 0 cnt[c] = 0 c n t [ c ] = 0 ,则表明此种类的书在当前区间未出现过,此时 $cnt[c] ++ $ 的同时,还需要 $diff ++ $;若 c n t [ c ] ≠ 0 cnt[c] \not = 0 c n t [ c ] = 0 ,则表明已经出现过,只需要 $cnt[c] ++ $。
接着移动滑动窗口,对于新进入窗口的数 a [ r ] a[r] a [ r ] ,如果 c n t [ a [ r ] ] = 0 cnt[a[r]] = 0 c n t [ a [ r ]] = 0 ,需要 $diff ++ $。对于离开窗口的数 a [ l − 1 ] a[l - 1] a [ l − 1 ] ,如果 $cnt[a[l-1]] – $ 后,c n t [ a [ l − 1 ] ] = 0 cnt[a[l - 1]] = 0 c n t [ a [ l − 1 ]] = 0 ,此时说明区间内不再有种类为 a [ l − 1 ] a[l - 1] a [ l − 1 ] 的书,故需要进行 $diff – $。
对于当前的区间 [ l , r ] [l, r] [ l , r ] , 处理完这些操作后,若 d i f f = m diff = m d i ff = m ,则包含了所有种类的书,此时就返回起点 l l l (由于是顺序枚举的,第一个满足的 l l l 就是答案)。如果枚举到最后都没有满足的 l l l ,则返回 0 0 0 。
最终时间复杂度为 O ( n log n ) O(n \log n) O ( n log n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedef long long ll;const int N = 1e6 + 10 , M = 2010 ;int n, m;int a[N], cnt[M];int check (int mid) { memset (cnt, 0 , sizeof cnt); int l = 1 , r = mid, diff = 0 ; for (int i = l; i <= r; i ++ ){ if (!cnt[a[i]]) diff ++ ; cnt[a[i]] ++ ; } if (diff == m) return l; for (l = 2 , r = mid + 1 ; r <= n; l ++ , r ++ ){ if (!cnt[a[r]]) diff ++ ; cnt[a[l - 1 ]] -- , cnt[a[r]] ++ ; if (cnt[a[l - 1 ]] == 0 ) diff -- ; if (diff == m) return l; } return 0 ; }int main () { ios; cin >> n >> m; for (int i = 1 ; i <= n; i ++ ) cin >> a[i]; int l = m, r = n; while (l < r){ int mid = l + r >> 1 ; if (check (mid)) r = mid; else l = mid + 1 ; } int s = check (r), e = s + r - 1 ; cout << s << ' ' << e << endl; return 0 ; }
解法二 (双指针)
可以用双指针遍历数组来记录所有满足条件的区间 [ l , r ] [l, r] [ l , r ] 。我们还是需要一个计数数组 c n t cnt c n t 和一个计数变量 d i f f diff d i ff ,c n t cnt c n t 数组统计的是 每个种类的书出现的次数 ,d i f f diff d i ff 统计的是 当前区间内不同种类的书的个数 。
如果当前 d i f f < m diff < m d i ff < m ,此时右指针 r r r 往后移动,并判断 $r ++ $ 后的 a [ r ] a[r] a [ r ] 是否出现过,同时增加计数。
如果当前 d i f f = m diff = m d i ff = m ,此时的区间 [ l , r ] [l, r] [ l , r ] 是满足要求的,故更新答案。我们注意到当前 r r r 之后所有的 r ′ r^{'} r ′ 与当前的 l l l 所构成的区间 [ l , r ′ ] [l, r^{'}] [ l , r ′ ] 也都一定是满足条件的,而且区间长度还一定比当前的 [ l , r ] [l, r] [ l , r ] 更大。
所以,为了省去这些多余的情况,我们这时候需要将 a [ l ] a[l] a [ l ] 从区间中移除,也就是左指针 l l l 往后走,并更新区间中的计数。遍历完后得到的答案就是最短且 l l l 最小的区间。时间复杂度为 O ( n ) O(n) O ( n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f ;int n, m;int a[N], cnt[M];int main () { ios; cin >> n >> m; for (int i = 1 ; i <= n; i ++ ) cin >> a[i]; int l = 1 , r = l, diff = 1 , res = inf, res_l, res_r; cnt[a[1 ]] = 1 ; while (l <= r && r <= n){ if (diff == m){ if (res > r - l + 1 ){ res = r - l + 1 ; res_l = l, res_r = r; } cnt[a[l]] -- ; if (!cnt[a[l ++ ]]) diff -- ; }else { if (!cnt[a[ ++ r]]) diff ++ ; cnt[a[r]] ++ ; } } cout << res_l << ' ' << res_r << '\n' ; return 0 ; }
E. 时间冻结
题目描述
N N N 个点,M M M 条无向边,可以 最多 选择 K K K 条边将其长度缩减为原先长度的一半,求 1 1 1 到 N N N 的最短距离。(边的长度一定是偶数,所以不用担心出现浮点数情况)「YAC Round 1」时间冻结
解法一 (dijkstra + DP 最短路)
K K K 的范围很小,故可以在更新最短路的时候记录使用的符卡数量,这是典型的 D P DP D P 最短路做法。
d i s t [ v ] [ c n t ] dist[v][cnt] d i s t [ v ] [ c n t ] 表示 使用了 c n t cnt c n t 张符卡状态下 1 1 1 到 v v v 的最短距离。走一遍最短路算法,最后枚举一下使用不同符卡数量到达重点 N N N 的距离取最小值即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int N = 60 , M = 2010 , inf = 0x3f3f3f3f ;int h[N], e[M], ne[M], w[M], idx; void add (int a, int b, int c) { e[ ++ idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx; }int n, m, K;int dist[N][N]; bool st[N][N]; struct node {int d, u, cnt;}; struct cmp { bool operator () (const node &a, const node &b) { return a.d > b.d; } };void dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ][0 ] = 0 ; priority_queue<node, vector<node>, cmp > q; q.push (node{dist[1 ][0 ], 1 , 0 }); while (q.size ()){ auto [d, u, cnt] = q.top (); q.pop (); if (st[u][cnt]) continue ; st[u][cnt] = true ; for (int i = h[u]; i; i = ne[i]){ int v = e[i], c = w[i]; if (dist[v][cnt] > d + c){ dist[v][cnt] = d + c; q.push (node{dist[v][cnt], v, cnt}); } if (cnt < K){ if (dist[v][cnt + 1 ] > d + (c >> 1 )){ dist[v][cnt + 1 ] = d + (c >> 1 ); q.push (node{dist[v][cnt + 1 ], v, cnt + 1 }); } } } } }int main () { ios; cin >> n >> m >> K; while (m -- ){ int u, v, c; cin >> u >> v >> c; add (u, v, c), add (v, u, c); } dijkstra (); int res = inf; for (int i = 0 ; i <= K; i ++ ) res = min (res, dist[n][i]); cout << res << endl; return 0 ; }
解法二 (dijkstra + 分层图)
我们可以通过构建分层图,在分层图上跑一遍最短路即可。
对于一条边 { u , v , w } \{ u, v, w\} { u , v , w } ,每个点构建 K + 1 K + 1 K + 1 个点。同层的点 u + k × n u + k \times n u + k × n 和 v + k × n v + k \times n v + k × n (0 ≤ k ≤ K 0 \le k \le K 0 ≤ k ≤ K )之间建立长度为 w w w 的边;相邻层的点 u + k × n u + k \times n u + k × n 和 v + ( k + 1 ) × n v + (k + 1) \times n v + ( k + 1 ) × n ,建立长度为 w 2 \frac{w}{2} 2 w 的边,表示这条边上使用一次符卡。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedef pair<int , int > pii;const int N = 3010 , M = 1e6 + 10 , inf = 0x3f3f3f3f ;int h[N], e[M], ne[M], w[M], idx; void add (int a, int b, int c) { e[ ++ idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx; }int n, m, K, dist[N];bool st[N]; void dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; priority_queue<pii, vector<pii>, greater<pii> > q; q.push (make_pair (dist[1 ], 1 )); while (q.size ()){ auto [d, u] = q.top (); q.pop (); if (st[u]) continue ; st[u] = true ; for (int i = h[u]; i; i = ne[i]){ int v = e[i], c = w[i]; if (dist[v] > d + c){ dist[v] = d + c; q.push (make_pair (dist[v], v)); } } } }int main () { ios; cin >> n >> m >> K; while (m -- ){ int u, v, c; cin >> u >> v >> c; for (int k = 0 ; k <= K; k ++ ) add (u + k * n, v + k * n, c), add (v + k * n, u + k * n, c); for (int k = 0 ; k < K; k ++ ) add (u + k * n, v + (k + 1 ) * n, c / 2 ), add (v + k * n, u + (k + 1 ) * n, c / 2 ); } dijkstra (); int res = inf; for (int k = 0 ; k <= K; k ++ ) res = min (res, dist[n + k * n]); cout << res << '\n' ; return 0 ; }
F. 卫星露天咖啡座
题目描述
共 Q Q Q 次操作,每次操作包含一个日期 a a a 、一个奶牛编号 b b b ,以及一个产奶变化量 c c c 。需要维护所有奶牛中最大产奶量奶牛的编号(奶牛的数量未知)。求出所有操作结束后一共修改最大产奶量奶牛的编号的次数。「YAC Round 1」卫星露天咖啡座
解题思路 (线段树)
我们可以用 线段树 来维护所有奶牛的最大值以及最大值对应的编号。
由于操作的日期是乱序的,故我们需要先根据操作的日期对操作进行排序。但是,奶牛的数量是未知的,而且题中说明一定存在若干奶牛产奶量保持在 G G G ,所以,我们需要增加一个虚拟的操作 { 0 , 0 , 0 } \{0, 0, 0\} { 0 , 0 , 0 } ,预先加入一个产奶量不变的 0 0 0 号奶牛。
奶牛编号范围为 [ 1 , 1 0 9 ] [1, 10^9] [ 1 , 1 0 9 ] ,但是操作数量为 [ 1 , 1 0 6 ] [1, 10^6] [ 1 , 1 0 6 ] ,为了维护奶牛区间,需要对奶牛编号进行离散化。
对于维护最大产奶量奶牛的编号,我们不仅需要用线段树维护区间最值和对应编号,还需要维护最大产奶量奶牛的数量,因为题中表明,当有若干头奶牛的产奶量最高,需要展示 所有 的最高产奶量奶牛的编号。
也就是说,修改奶牛编号操作涉及两种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define ls (u << 1) #define rs (u << 1 | 1) const int N = 1e6 + 10 ;struct qu {int t, id, d;}; vector<qu> ques; int n, g; vector<int > v;int find (int x) {return lower_bound (v.begin (), v.end (), x) - v.begin ();}int mx[N << 2 ], cnt[N << 2 ], id[N << 2 ]; void pushup (int u, int l, int r) { if (mx[ls] > mx[rs]){ mx[u] = mx[ls]; cnt[u] = cnt[ls]; id[u] = id[ls]; }else if (mx[ls] < mx[rs]){ mx[u] = mx[rs]; cnt[u] = cnt[rs]; id[u] = id[rs]; }else { mx[u] = mx[ls]; cnt[u] = cnt[ls] + cnt[rs]; id[u] = id[ls]; } }void build (int u, int l, int r) { mx[u] = g; if (l == r){cnt[u] = 1 , id[u] = l; return ;} int mid = l + r >> 1 ; build (ls, l, mid); build (rs, mid + 1 , r); pushup (u, l, r); }void update (int u, int l, int r, int k, int d) { if (l == r){mx[u] += d; return ;} int mid = l + r >> 1 ; if (k <= mid) update (ls, l, mid, k, d); else update (rs, mid + 1 , r, k, d); pushup (u, l, r); }int main () { ios; cin >> n >> g; for (int i = 1 , t, id, d; i <= n; i ++ ){ string s; cin >> t >> id >> s; d = (s[0 ] == '+' ? stoi (s.substr (1 )) : stoi (s)); ques.push_back (qu{t, id, d}); v.push_back (id); } ques.push_back (qu{0 , 0 , 0 }); v.push_back (0 ); sort (ques.begin (), ques.end (), [](qu &a, qu &b){ return a.t < b.t; }); sort (v.begin (), v.end ()); v.erase (unique (v.begin (), v.end ()), v.end ()); int m = (int )v.size (); build (1 , 1 , m); int pre_id, pre_cnt, res = 0 ; for (auto &p : ques){ pre_id = id[1 ], pre_cnt = cnt[1 ]; update (1 , 1 , m, find (p.id), p.d); int now_id = id[1 ], now_cnt = cnt[1 ]; res += (now_id != pre_id || now_cnt != pre_cnt); } cout << res << '\n' ; return 0 ; }