VP,练习。
 
1. 移动 模拟题意即可。
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 #include <bits/stdc++.h>  using  namespace  std;void  solve ()     int  n, k; cin >> n >> k;     while (k--){         int  x, y; cin >> x >> y;         string s; cin >> s;         for (auto  &i : s){             if (i == 'f' ){                 y = min (n, y + 1 );             }             else  if (i == 'b' ){                 y = max (1 , y - 1 );             }             else  if (i == 'l' ){                 x = max (1 , x - 1 );             }             else  {                 x = min (n, x + 1 );             }         }         cout << x << ' '  << y << '\n' ;     } } signed  main ()     ios::sync_with_stdio (0 );     cin.tie (0 );     solve ();     return  0 ; } 
2. 梦境巡查 为了顺利完成巡查,需要满足的条件有:
$w-a_0\ge 0$ 
$w+b_1-(a_0+a_1)\ge0$ 
$\cdots$ 
$w+\sum\limits_{i=1}^{n}b_i-\sum\limits_{i=0}^na_i\ge0$ 
 
即:对于任意的 $k\in[0,n]$ ,都有 $w\ge\sum\limits_{i=0}^ka_i-\sum\limits_{i=1}^{k}b_i$
$w$ 最小时,有 $w=\max\limits_{0\le k\le n}\left(\sum\limits_{i=0}^ka_i-\sum\limits_{i=1}^{k}b_i\right)$。
维护住 $c_k=\sum\limits_{i=0}^ka_i-\sum\limits_{i=1}^{k}b_i$,现在考虑让 $b_i$ 依次变为 $0$。
不难发现。令 $b_i=0$ 时,$c_i, c_{i+1},\cdots,c_n$ 都会增大 $b_i$。 
此时的全局最大值,只需提前维护住 $c$ 数组中每个位置的前缀最大值和后缀最大值,计算 $\max(premax[i - 1],sufmax[i] + b_i)$ 即可。 
 
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 #include <bits/stdc++.h>  #define  int long long using  namespace  std;void  solve ()     int  n; cin >> n;     vector<int >a (n + 1 );     for (int  i = 0 ; i <= n; i++) cin >> a[i];     vector<int >b (n + 1 );     for (int  i = 1 ; i <= n; i++) cin >> b[i];     vector<int >c (n + 1 );     c[0 ] = a[0 ];     for (int  i = 1 ; i <= n; i++){         c[i] = c[i - 1 ] + a[i] - b[i];     }	     vector<int >pre (n + 1 ), suf (n + 1 );     pre[0 ] = c[0 ];     for (int  i = 1 ; i <= n; i++){         pre[i] = max (pre[i - 1 ], c[i]);     }     suf[n] = c[n];     for (int  i = n - 1 ; i >= 0 ; i--){         suf[i] = max (suf[i + 1 ], c[i]);     }     for (int  i = 1 ; i <= n; i++){         cout << max (pre[i - 1 ], suf[i] + b[i]) << ' ' ;     } } signed  main ()     ios::sync_with_stdio (0 );     cin.tie (0 );     solve ();     return  0 ; } 
3. 缓存模拟 大模拟,按题意自行梳理流程。
唯一关键的是数据结构的选择。我们需要维护每一个组,满足:
组中的数据按照使用的时间先后排序。 
对于每一个数据,我们需要能快速判断,组中是否存在该数据。(即判断是否命中) 
需要能快速找到数据在组中的位置,并将其提到队首。(更新最后使用时间) 
需要能快速弹出队末的元素(替换)。 
 
那么用 set 维护每一个组,并重载比较方式即可。
详细流程可以见代码,非常清晰。
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 #include <bits/stdc++.h>  #define  int long long using  namespace  std;unordered_map<int , int >t; unordered_map<int , bool >cg; struct  cmpbytime  {    bool  operator () (const  int  &a, const  int  &b)  const           return  t[a] < t[b];     } }; void  solve ()     int  n, N, q; cin >> n >> N >> q;          vector<set<int , cmpbytime>>Q (N);     for (int  k = 1 ; k <= q; k++){         int  o, a; cin >> o >> a;         int  p = (a / n) % N;                   auto  u = Q[p].find (a);                  if (u != Q[p].end ()){                          int  v = *u;             Q[p].erase (u);             t[v] = k;             Q[p].emplace (v);                          if (o == 1 ) cg[v] = 1 ;         }                  else {                          if (Q[p].size () == n){                 int  v = *Q[p].begin ();                                  if (cg[v]) {                     cout << "1 "  << v << '\n' ;                     cg[v] = 0 ;                 }                 Q[p].erase (v);             }                          cout << "0 "  << a << '\n' ;              t[a] = k;             Q[p].emplace (a);                          if (o == 1 ) cg[a] = 1 ;         }     } } signed  main ()     ios::sync_with_stdio (0 );     cin.tie (0 );     solve ();     return  0 ; } 
4. 跳房子 
这题似乎解法非常丰富,这里考虑了一些从最短路方向思考的解法。
 
为描述方便,我们称一次跳跃的目标位置为 “一次落地点”,接着后退 a[i] 格,落点称为 “二次落地点”。
首先考虑一个显然的 $O(n^2)$ 的解法:将每一个点与所有 “二次落地点” 建边。那么 $n$ 个点,最多 $n(n-1)$ 条(单向)边,直接 BFS 求最短路即可。
不难发现,这个解法的瓶颈在于边数是 $O(n^2)$ 的,如果我们能减少连边,就能解决这个问题了。
优化思路一:set 维护 “一次落地点” 不难发现,BFS 过程中,已经成为 “一次落地点” 的点是没有再次成为 “一次落地点” 的必要的,我们用一个 set 维护所有还没有成为 “一次落地点”  的点。我们每次跳跃只从 set 中选取 “一次落地点” 来跳;每当一个点成为了 “一次落地点”,我们就将其从 set 中删去。这样,我们连边就不会超过 $n$ 条。
时间复杂度 $O(n\log n)$,瓶颈在 set 的删点。(感觉是最清爽也最高效的优化思路了)
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 #include <bits/stdc++.h>  #define  int long long using  namespace  std;typedef  pair<int , int > PII;constexpr  int  inf = 1e9 ;inline  int  read ()     int  x = 0 , f = 1 ; char  ch = getchar ();     while (ch < '0'  || ch > '9' ) { if (ch == '-' ) f = -1 ; ch = getchar (); }     while (ch >= '0'  && ch <= '9' ) { x = x * 10  + ch - 48 ; ch = getchar (); }     return  x * f; } void  solve ()     int  n = read ();     vector<int >a (n + 1 ), k (n + 1 );     for (int  i = 1 ; i <= n; i++) a[i] = read ();     for (int  i = 1 ; i <= n; i++) k[i] = read ();     set<int >st;     for (int  i = 2 ; i <= n; i++) st.emplace (i);     queue<int >Q;     vector<int >dist (n + 1 , inf);     Q.emplace (1 );     dist[1 ] = 0 ;     while (!Q.empty ()){         int  u = Q.front ();         Q.pop ();         vector<int >del;         for (auto  it = st.lower_bound (u + 1 ); it != st.end () && *it <= min (n, u + k[u]); it++){             del.emplace_back (*it);             int  v = *it - a[*it];             if (dist[v] == inf) {                 dist[v] = dist[u] + 1 ;                 Q.emplace (v);             }         }         for (auto  &i : del) st.erase (i);     }     if (dist[n] == inf){         cout << -1  << '\n' ;     }     else {		         cout << dist[n] << '\n' ;     } } signed  main ()               solve ();     return  0 ; } 
优化思路二:分块优化建边 
注意这一份并不能通过所有测试数据。
 
可以利用分块优化连边,将边数降到 $O(n\sqrt n)$ 级别。
具体做法为,为每个 $\sqrt n$ 的分块新建一个父节点,当某个点需要连的边包含完整分块时,直接连接这些分块的父节点(边权为0)。
最后,执行 01BFS 即可。总时间复杂度 $O(n\sqrt n)$ ,讲题嘉宾说该思路能过,但这份 TLE 了(懂的教教)。 
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 #include <bits/stdc++.h>  using  namespace  std;typedef  pair<int , int > PII;constexpr  int  inf = 1e9 ;inline  int  read ()     int  x = 0 , f = 1 ; char  ch = getchar ();     while (ch < '0'  || ch > '9' ) { if (ch == '-' ) f = -1 ; ch = getchar (); }     while (ch >= '0'  && ch <= '9' ) { x = x * 10  + ch - 48 ; ch = getchar (); }     return  x * f; } void  solve ()     int  n = read ();     vector<int >a (n), k (n);     for (int  i = 0 ; i < n; i++) a[i] = read ();     for (int  i = 0 ; i < n; i++) k[i] = read ();     int  bs = sqrt (n);     int  bn = (n + bs - 1 ) / bs;     vector<vector<PII>>adj (n + bn);     for (int  i = 0 ; i < bn; i++){         for (int  j = i * bs; j < min (n, (i + 1 ) * bs); j++){             adj[n + i].emplace_back (1 , j);         }     }     for (int  i = 0 ; i < n; i++){         int  bl = (i + 1 ) / bs, br = min (n - 1 , i + k[i]) / bs;                  if (bl == br){             for (int  j = i + 1 ; j <= min (n - 1 , i + k[i]); j++){                 adj[i].emplace_back (1 , j);             }         }         else {             for (int  j = bl + 1 ; j < br; j++){                 adj[i].emplace_back (0 , n + j);             }             for (int  j = i + 1 ; j / bs == bl; j++){                 adj[i].emplace_back (1 , j);             }			             for (int  j = min (n - 1 , i + k[i]); j / bs == br; j--){                 adj[i].emplace_back (1 , j);             }         }     }     deque<int >Q;     vector<int >dist (n + bn, inf);     Q.emplace_back (0 );     dist[0 ] = 0 ;     while (!Q.empty ()){         int  u = Q.front ();         Q.pop_front ();         for (auto  &[w, j] : adj[u]){             int  v = w ? j - a[j] : j;             if (dist[v] == inf){                 dist[v] = dist[u] + w;                 if (w == 1 ){                     Q.emplace_back (v);                 }                 else {                     Q.emplace_front (v);                 }                 if (v == n - 1 ){                     cout << dist[v];                     return ;                 }             }         }     }     cout << -1 ; } signed  main ()     solve ();     return  0 ; } 
优化思路三:线段树优化建边 边数优化到 $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 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 #include <bits/stdc++.h>  using  namespace  std;typedef  pair<int , int > PII;constexpr  int  inf = 1e9 ;inline  int  read ()     int  x = 0 , f = 1 ; char  ch = getchar ();     while (ch < '0'  || ch > '9' ) { if (ch == '-' ) f = -1 ; ch = getchar (); }     while (ch >= '0'  && ch <= '9' ) { x = x * 10  + ch - '0' ; ch = getchar (); }     return  x * f; } struct  SegNode  {    int  l, r;     int  left, right; }; vector<SegNode> seg_tree; int  n;int  build (int  l, int  r, vector<vector<PII>>& adj)      if  (l > r) return  -1 ;     int  id = n++;     seg_tree.push_back ({l, r, -1 , -1 });     if  (l == r) {                  adj[id].emplace_back (1 , l);         return  id;     }     int  mid = (l + r) >> 1 ;     int  left = build (l, mid, adj);     int  right_child = build (mid+1 , r, adj);     seg_tree[id - n].left = left;     seg_tree[id - n].right = right_child;          if  (left != -1 )         adj[id].emplace_back (0 , left);     if  (right_child != -1 )         adj[id].emplace_back (0 , right_child);     return  id; } void  query (int  u, int  l, int  r, vector<int >& res)      const  SegNode& node = seg_tree[u - n];     if  (node.r < l || node.l > r) return ;     if  (l <= node.l && node.r <= r) {         res.push_back (u);         return ;     }     if  (node.left != -1 ) query (node.left, l, r, res);     if  (node.right != -1 ) query (node.right, l, r, res); } void  solve ()      int  n = read ();     vector<int > a (n) , k (n)  ;     for  (int  i = 0 ; i < n; ++i) a[i] = read ();     for  (int  i = 0 ; i < n; ++i) k[i] = read ();          seg_tree.clear ();     int  max_seg_size = 4  * n;     vector<vector<PII>> adj (n + max_seg_size);     int  root = build (0 , n - 1 , adj);          for  (int  i = 0 ; i < n; ++i) {         int  L = i + 1 ;         int  R = i + k[i];         if  (L >= n) continue ;         R = min (R, n-1 );         if  (L > R) continue ;         vector<int > nodes;         query (root, L, R, nodes);         for  (int  u : nodes) {             adj[i].emplace_back (0 , u);         }     }          vector<int > dist (n + max_seg_size, inf)  ;     deque<int > q;     dist[0 ] = 0 ;     q.push_back (0 );     while  (!q.empty ()) {         int  u = q.front ();         q.pop_front ();         if  (u == n-1 ) {             cout << dist[u] << endl;             return ;         }         for  (auto & [w, v] : adj[u]) {             int  target = (w == 1 ) ? (v - a[v]) : v;             if  (dist[target] > dist[u] + w) {                 dist[target] = dist[u] + w;                 if  (w == 0 ) {                     q.push_front (target);                 } else  {                     q.push_back (target);                 }             }         }     }     cout << -1  << endl; } signed  main ()      solve ();     return  0 ; }