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 () 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);'\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 () 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); '\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 () int  T = 1 ; cin >> T;while (T -- ){if (n < 2  * p + 1 ){ -1  << '\n' ;continue ;"" ;for (int  i = 2  * p + 1 ; i < n; i ++ ) res += "0" ; for (int  i = 1 ; i <= p; i ++ ) res += "10" ;       "1" ;  '\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;26 ]; int  main () " "  + s;for (int  i = 1 ; i <= n; i ++ )for (int  j = 0 ; j < 26 ; j ++ )1 ][j] + (s[i] - 'a'  == j);0 ;for (int  i = 3 ; i <= n; i ++ )for (int  j = 0 ; j < 26 ; j ++ )if (j != s[i] - 'a' ) 1 ][j] * (cnt[i - 1 ][j] - 1 ) / 2 ;'\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;26 ];int  main () " "  + s;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 ;'a' ] ++ ;'\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 c n t [ c ] ≠ 0 cnt[c] \not = 0 c n t [ c ]  = 0 
接着移动滑动窗口,对于新进入窗口的数 a [ r ] a[r] a [ r ] c n t [ a [ r ] ] = 0 cnt[a[r]] = 0 c n t [ a [ r ]] = 0 a [ l − 1 ] a[l - 1] 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 ] 
对于当前的区间 [ 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 ++ ;if (diff == m) return  l; for (l = 2 , r = mid + 1 ; r <= n; l ++ , r ++ ){if (!cnt[a[r]]) diff ++ ;          1 ]] -- , cnt[a[r]] ++ ; if (cnt[a[l - 1 ]] == 0 ) diff -- ;  if (diff == m) return  l;return  0 ;int  main () 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 ; ' '  << 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 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 () for (int  i = 1 ; i <= n; i ++ ) cin >> a[i];int  l = 1 , r = l, diff = 1 , res = inf, res_l, res_r;1 ]] = 1 ;while (l <= r && r <= n){if (diff == m){if (res > r - l + 1 ){  1 ;if (!cnt[a[l ++ ]]) diff -- ; else {if (!cnt[a[ ++ r]]) diff ++ ; ' '  << 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) 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);1 ][0 ] = 0 ;push (node{dist[1 ][0 ], 1 , 0 });while (q.size ()){auto  [d, u, cnt] = q.top ();pop ();if (st[u][cnt]) continue ;true ;for (int  i = h[u]; i; i = ne[i]){int  v = e[i], c = w[i];if (dist[v][cnt] > d + c){ push (node{dist[v][cnt], v, cnt});if (cnt < K){ if (dist[v][cnt + 1 ] > d + (c >> 1 )){ 1 ] = d + (c >> 1 );push (node{dist[v][cnt + 1 ], v, cnt + 1 });int  main () 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]);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) int  n, m, K, dist[N];bool  st[N]; void  dijkstra () memset (dist, 0x3f , sizeof  dist);1 ] = 0 ;push (make_pair (dist[1 ], 1 ));while (q.size ()){auto  [d, u] = q.top ();pop ();if (st[u]) continue ;true ;for (int  i = h[u]; i; i = ne[i]){int  v = e[i], c = w[i];if (dist[v] > d + c){ push (make_pair (dist[v], v));int  main () 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]);'\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;};int  n, g; 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]){  else  if (mx[ls] < mx[rs]){  else { void  build (int  u, int  l, int  r) 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 () for (int  i = 1 , t, id, d; i <= n; i ++ ){0 ] == '+'  ? stoi (s.substr (1 )) : stoi (s)); push_back (qu{t, id, d});push_back (id);push_back (qu{0 , 0 , 0 }); push_back (0 );   sort (ques.begin (), ques.end (), [](qu &a, qu &b){return  a.t < b.t;  sort (v.begin (), v.end ());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){1 ], pre_cnt = cnt[1 ];  update (1 , 1 , m, find (p.id), p.d); int  now_id = id[1 ], now_cnt = cnt[1 ];  '\n' ;return  0 ;