2024年5月CSP校内选拔赛,回顾+题解。
 
    
    
得分245/400,AC了两个小难题,差强人意。
题解 A. 装饰品 考点:乘法原理,快速幂,费马小定理
题意:将 m 种元素填入 n 个盒子,要求不存在任何长度大于等于 2 的回文子串,求解填入的方法数。
分析:不难发现,只要满足不存在长度为 2,3 的回文子串,就不会出现回文子串。所以填入时只需保证填入的元素不与前两个位置相同即可。即 $m\times (m-1)\times(m-2)^{n-2}$ 对 1e9 + 7 取模为所求。
实际上,n 与 m 范围过大,需要考虑多个优化:
费马小定理:
 
完整代码: 
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 #include <bits/stdc++.h>  using  namespace  std;#define  int unsigned long long const  int  p = 1e9  + 7 ;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; } int  qmi (int  a, int  k)     int  res = 1 ;     while (k){         if (k & 1 ) res = res * a % p;         k >>= 1 ;          a = a * a % p;     }        return  res; } signed  main ()     ios::sync_with_stdio (0 );     cin.tie (0 ); cout.tie (0 );     int  t = read ();     while (t--){         int  n = read () % (p - 1 ), m = read () % p;         if (n == 1 ){             cout << m % p << '\n' ;         }         else {             int  ans = ((m % p) * ((m - 1 ) % p)) % p;             ans = (qmi (m - 2 , n - 2 ) * ans) % p;             cout << ans << '\n' ;         }     }     return  0 ; } 
B. 幻兽帕鲁 题意:给定长度为 n 的数列,在其中取数,规定不能连续取超过 k 个数,求取出数的和的最大值。
分析:dp[i][j] 表示第 i 天,连续取出 j 个数时,已取出数的最大值。dp[i][0] 由前一天的 dp 最大值决定。dp[i][j] = dp[i - 1][j - 1] + a[i]。50/100)
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 #include <bits/stdc++.h>  using  namespace  std;const  int  N = 1005 ;int  a[N], dp[N][N];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; } int  main ()     int  n = read (), k = read ();     for (int  i = 1 ; i <= n; i++)         a[i] = read ();              for (int  i = 1 ; i <= n; i++){         for (int  j = 0 ; j <= k; j++){             dp[i][0 ] = max (dp[i][0 ], dp[i - 1 ][j]);         }         for (int  j = 1 ; j <= k; j++){             dp[i][j] = dp[i - 1 ][j - 1 ] + a[i];         }     }          int  ans = 0 ;     for (int  i = 0 ; i <= k; i++){         ans = max (ans, dp[n][i]);     }     cout << ans;     return  0 ; } 
2)单调栈优化dp,不难发现,当dp[i][j]中,有 j1 < j2 且 dp[i][j1] > dp[i][j2] 时,dp[i][j_2]是不必考虑的。使用单调栈维护每一个dp[i]的情况即可。AC代码:
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 #include <bits/stdc++.h>  using  namespace  std;typedef  pair<long  long , int > PII;#define  int long long const  int  N = 1e5  + 10 ;int  a[N];vector<PII>dp[N]; 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; } signed  main ()      int  n = read (), k = read ();     for  (int  i = 1 ; i <= n; i++)         a[i] = read ();     dp[1 ].emplace_back (0 , 0 );     if  (a[1 ] > 0 ) dp[1 ].emplace_back (a[1 ], 1 );     for  (int  i = 2 ; i <= n; i++) {         auto  tmp = dp[i - 1 ].back ();         dp[i].emplace_back (tmp.first, 0 );         int  pos = upper_bound (dp[i - 1 ].begin (), dp[i - 1 ].end (), make_pair (dp[i][0 ].first - a[i], 1000000 )) - dp[i - 1 ].begin ();         for  (int  j = pos; j < dp[i - 1 ].size (); j++) {             if  (dp[i - 1 ][j].second != k)                 dp[i].emplace_back (dp[i - 1 ][j].first + a[i], dp[i - 1 ][j].second + 1 );         }     }     cout << dp[n].back ().first;     return  0 ; } 
补充 AC后非常满足,直到看到出题人题解(单调队列优化dp):
好一个正难则反。
C. 下水道鼠鼠 题意:图中一些点上给定若干块蛋糕(称为聚落),称在距离 s 内,存在蛋糕数为 s 的结点的点为宜居点,输出宜居点的个数及所有宜居点。
分析:考虑聚落向外扩散,初始化所有除聚落的点的宜居值为 -1,聚落的宜居值=蛋糕数量,开始时将所有点压入优先队列,向外更新宜居值即可。(感觉比前面两题简单www)
完整代码: 
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 #include <bits/stdc++.h>  using  namespace  std;typedef  pair<int , int > PII;const  int  N = 2e5  + 10 ;int  n, m, k;vector<int > g[N]; bool  vis[N]; priority_queue<PII> q; 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; } int  main ()      n = read (), m = read (), k = read ();     for  (int  i = 1 ; i <= m; i++) {         int  x = read (), y = read ();         g[x].emplace_back (y), g[y].emplace_back (x);     }     for  (int  i = 1 ; i <= k; i++) {         int  p = read (), s = read ();         q.emplace (s, p);     }     while  (!q.empty ()) {         int  s = q.top ().first, u = q.top ().second;         q.pop ();         if  (vis[u]) continue ;         vis[u] = 1 ;         if  (s == 0 ) continue ;         for  (auto & v : g[u]) {             if  (!vis[v]) {                 q.emplace (s - 1 , v);             }         }     }     int  res = 0 ;     for  (int  i = 1 ; i <= n; i++)         if  (vis[i]) res++;     cout << res << '\n' ;     for  (int  i = 1 ; i <= n; i++)         if  (vis[i]) cout << i << ' ' ;     return  0 ; }