题号 标题 已通过代码 通过数 tag 难度
A R 点击查看 837 前缀和``贪心 1500
B 进制 点击查看 169 线段树 1900
C 蓝彗星 点击查看 1153 前缀和 1400
D 雪色光晕 点击查看 488 计算几何 1700
E 真假签到题 点击查看 2491 数学 1200
F 小红的记谱法 点击查看 1520 栈``模拟 1400
G 子序列权值乘积 点击查看 250 数学``思维 1800
H 真真真真真签到题 点击查看 2545 数学 1200
I 爆炸的符卡洋洋洒洒 点击查看 552 DP 1700
J 区间合数的最小公倍数 点击查看 746 数论 1600
K 小红的真真假假签到题题 点击查看 1597 数学 1400
L 在这冷漠的世界里光光哭哭 点击查看 15 前缀和``离散化``计数 2000

总体:难度偏易

A

通过前缀和,计算 pos[i], 表示第 i 个 R 出现的最早位置。
从左到右遍历,当前位置为 i 时,假设出现 num 个 R,上一次在 last 位置出现了 P。那么通过 pos[num - k + 1] 可以获得满足 k 个R的最早位置。
那么可选位置最多只会有 pos[num - k + 1] - last 个,这个数值可能会小于 0,所以与 0 取 max即可。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
  4. void err() { cout << "\033[39;0m" << endl; }
  5. template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
  6. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  7. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  8. typedef long long ll;
  9. const int N = 200010;
  10. int n, k;
  11. char s[N];
  12. int pos[N];
  13. int main(){
  14. cin >> n >> k;
  15. scanf("%s", s + 1);
  16. int num = 0, last = 0;
  17. ll rs = 0;
  18. pos[0] = 0;
  19. rep(i,1,n) {
  20. if(s[i] == 'R') {
  21. num ++;
  22. pos[num] = i;
  23. }
  24. if(s[i] == 'P') last = i;
  25. if(num >= k) {
  26. rs += max(0, pos[num - k + 1] - last);
  27. // dbg(i, last,pos[num - k + 1], max(0, pos[num - k + 1] - last));
  28. }
  29. }
  30. printf("%lld\n",rs);
  31. return 0;
  32. }

B

线段树
线段树维护。
每个端点保存2~10进制下的具体数值、区间长度、区间最大值。
端点合并时,相应的每个进制进行拼接。
复杂度 第四场 02/09 - 图1

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
  4. void err() { cout << "\033[39;0m" << endl; }
  5. template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
  6. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  7. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  8. typedef long long ll;
  9. const int N = 100010, mod = 1e9 + 7;
  10. int n, q, p[11][N];
  11. char s[N];
  12. struct SegTree{
  13. ll val[11];
  14. int mx, len;
  15. }t[N*4];
  16. SegTree operator + (SegTree ls, SegTree rs) {
  17. SegTree t;
  18. t.mx = max(ls.mx, rs.mx);
  19. rep(i,2,10) {
  20. t.val[i] = ls.val[i] * p[i][rs.len] % mod + rs.val[i];
  21. t.val[i] %= mod;
  22. }
  23. t.len = ls.len + rs.len;
  24. return t;
  25. }
  26. void build(int p, int l, int r) {
  27. if(l == r) {
  28. t[p].mx = s[l] - '0';
  29. t[p].len = 1;
  30. rep(i,2,10) {
  31. t[p].val[i] = t[p].mx;
  32. }
  33. return;
  34. }
  35. int m = (l + r) / 2;
  36. build(p * 2, l, m);
  37. build(p * 2 + 1, m + 1, r);
  38. t[p] = t[p * 2] + t[p * 2 + 1];
  39. }
  40. void change(int p, int l, int r, int x, int val) {
  41. if(l == r) {
  42. t[p].mx = val;
  43. rep(i,2,10) {
  44. t[p].val[i] = t[p].mx;
  45. }
  46. return;
  47. }
  48. int m = (l + r) / 2;
  49. if(x <= m) change(p * 2, l, m, x, val);
  50. else change(p * 2 + 1, m + 1, r, x, val);
  51. t[p] = t[p*2] + t[p*2+1];
  52. }
  53. int queryMx(int p, int l, int r, int x, int y){
  54. if(l >= x && r <= y) return t[p].mx;
  55. int m = (l + r) / 2;
  56. if(x > m) return queryMx(p * 2 + 1, m + 1, r, x, y);
  57. else if(y <= m) return queryMx(p * 2, l, m, x, y);
  58. else return max(queryMx(p * 2, l, m, x, y), queryMx(p * 2 + 1, m + 1, r, x, y));
  59. }
  60. SegTree query(int p, int l, int r, int x, int y){
  61. if(l >= x && r <= y) return t[p];
  62. int m = (l + r) / 2;
  63. if(x > m) return query(p * 2 + 1, m + 1, r, x, y);
  64. else if(y <= m) return query(p * 2, l, m, x, y);
  65. else return query(p * 2, l, m, x, y) + query(p * 2 + 1, m + 1, r, x, y);
  66. }
  67. int main(){
  68. scanf("%d%d", &n, &q);
  69. scanf("%s", s + 1);
  70. rep(i,2,10) {
  71. p[i][0] = 1;
  72. rep(j,1,n) {
  73. p[i][j] = 1ll * p[i][j-1] * i % mod;
  74. }
  75. }
  76. build(1, 1, n);
  77. while(q--){
  78. int op, x, y;
  79. scanf("%d%d%d", &op, &x, &y);
  80. if(op == 1) {
  81. change(1, 1, n, x, y);
  82. } else {
  83. int mx = queryMx(1, 1, n, x, y) + 1;
  84. // dbg(mx);
  85. printf("%lld\n", query(1, 1, n, x, y).val[mx]);
  86. }
  87. }
  88. }

C

前缀和处理即可。
坑点是坐标范围,t 和 a 都是 100000,所以最大范围会到 200000

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  4. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  5. typedef long long ll;
  6. const int N = 200010;
  7. int n, t, a, c[N], d[N];
  8. char s[N];
  9. int main(){
  10. cin >> n >> t;
  11. scanf("%s", s + 1);
  12. rep(i,1,n) {
  13. scanf("%d", &a);
  14. if(s[i] == 'B') {
  15. c[a] += 1;
  16. c[a + t] -= 1;
  17. } else {
  18. d[a] += 1;
  19. d[a + t] -= 1;
  20. }
  21. }
  22. int rs = 0;
  23. rep(i,1,200000) {
  24. c[i] += c[i-1];
  25. d[i] += d[i-1];
  26. if(c[i] && !d[i]) rs ++;
  27. }
  28. cout << rs << endl;
  29. return 0;
  30. }

D

题目问运动过程中距离一个点的最短距离。
而每次运动轨迹是一条线段,所以这个题目就是点到线段的最短距离。

E

直接输出 n 即可。
可以用数学归纳法或者打表发现规律。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  4. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  5. typedef long long ll;
  6. long long f(long long x){
  7. if(x==1)return 1;
  8. return f(x/2)+f(x/2+x%2);
  9. }
  10. int main(){
  11. ll n;
  12. cin >> n;
  13. cout << n << endl;
  14. }

G

先整体排个序。思考如何 第四场 02/09 - 图2 内解决该题。
不难想出以下做法:

  1. for i in {1..n}:
  2. res *= (a[i] * a[i])
  3. for j in {1..i-1}:
  4. res *= (a[i] * a[j])^(2^(i-j-1))

这是由于 (i,j) 中间有 (j-i-1) 个数字,每个数字两种选择(出现或者不出现),会导致 (a[i], a[j]) 这对最大值与最小值的数量以 2 的幂次来计算。
那么现在问题转变成了如何快速计算上面的式子。
从最简单的,先来看 第四场 02/09 - 图3第四场 02/09 - 图4
再看 第四场 02/09 - 图5: 第四场 02/09 - 图6
第四场 02/09 - 图7: 第四场 02/09 - 图8

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
  4. void err() { cout << "\033[39;0m" << endl; }
  5. template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
  6. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  7. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  8. typedef long long ll;
  9. const int N = 200010, mod = 1e9 + 7;
  10. int n, a[N];
  11. ll ksm(ll a, ll b){
  12. ll rs = 1;
  13. for(;b;b>>=1) {
  14. if(b & 1) rs = rs * a % mod;
  15. a=a*a%mod;
  16. }
  17. return rs;
  18. }
  19. int main(){
  20. cin >> n;
  21. rep(i,1,n) {
  22. scanf("%d", &a[i]);
  23. }
  24. sort(a + 1, a + 1 + n);
  25. ll rs = 1, sum = 1, presum = 1, cnt = 1;
  26. rep(i,1,n) {
  27. sum = presum * a[i] % mod;
  28. presum = presum * presum % mod * a[i] % mod;
  29. rs = rs * sum % mod * ksm(a[i], cnt) % mod;
  30. cnt = (cnt * 2) % (mod - 1);
  31. }
  32. cout << rs << endl;
  33. return 0;
  34. }

H

高中数学题,给定正方体对角线的一般长度,求体积

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  4. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  5. typedef long long ll;
  6. int main(){
  7. double x;
  8. cin >> x;
  9. x = 8.0 * x * x * x / 3 / sqrt(3);
  10. printf("%.10f\n", x);
  11. }

I

先把 第四场 02/09 - 图9,然后 d[i][j] 表示前 i 张卡牌,使用了 j 的魔力可以达到的最大威力。其中 j < k,只记录模 k 后的值。
然后直接DP转移即可。复杂度 第四场 02/09 - 图10
对于每个状态 d[i][j],可以直接从 d[i-1][j] 转移过来。
然后对于 a[i],遍历每个 d[i-1][j], 更新到 d[i][(j + a[i])%k]。
由于求最大值,并且需要判断是否存在无解,所以一开始必须将所有初值赋值为负无穷。单独赋值 d[0][0] = 0
最后单独判断 d[n][0] 是否等于 0,是的话输出 -1

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  4. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  5. typedef long long ll;
  6. const int N = 1010;
  7. int n, k, a[N], b[N];
  8. ll d[N][N];
  9. int main(){
  10. scanf("%d%d", &n, &k);
  11. memset(d, -0x3f, sizeof d);
  12. d[0][0] = 0;
  13. rep(i,1,n) {
  14. scanf("%d%d", &a[i], &b[i]);
  15. a[i] %= k;
  16. memcpy(d[i], d[i-1], sizeof d[i]);
  17. rep(j, 0, k-1) {
  18. int p = (j + a[i]) % k;
  19. d[i][p] = max(d[i][p], d[i-1][j] + b[i]);
  20. }
  21. }
  22. if(d[n][0] == 0) d[n][0] = -1;
  23. printf("%lld\n", d[n][0]);
  24. }

J

最小公倍数的定理:
两个数分解质因数后:
第四场 02/09 - 图11
那么:
第四场 02/09 - 图12

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
  4. void err() { cout << "\033[39;0m" << endl; }
  5. template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
  6. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  7. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  8. typedef long long ll;
  9. const int mod = 1e9 + 7;
  10. unordered_map<int,int> mp;
  11. bool isPrime(int x){
  12. if(x == 1) return true;
  13. int t = sqrt(x);
  14. rep(i,2,t) if(x % i == 0) return false;
  15. return true;
  16. }
  17. void get(int x){
  18. for(int i = 2; i * i <= x; i++){
  19. if(x % i) continue;
  20. int c = 0;
  21. while(x % i == 0) {
  22. c ++;
  23. x /= i;
  24. }
  25. mp[i] = max(mp[i], c);
  26. }
  27. if(x > 1) mp[x] = max(mp[x], 1);
  28. }
  29. int main(){
  30. int l, r;
  31. cin >> l >> r;
  32. int g = 0;
  33. int sum = 1;
  34. int cnt = 0;
  35. rep(i,l,r) {
  36. if(isPrime(i)) continue;
  37. cnt ++;
  38. get(i);
  39. }
  40. if(cnt == 0) {
  41. puts("-1");
  42. return 0;
  43. }
  44. int rs = 1;
  45. for(auto t : mp) {
  46. while(t.second --) rs = 1ll * rs * t.first % mod;
  47. }
  48. cout << rs << endl;
  49. }

K

先求出 x 在二进制表示下,最高位的 1 的所在位置。
比如 5: 101,最高位 1 在第 3 位,那么将 5 左移 3 位再加上 5 即可。
可以证明这样的拼接方式一定不会超过 第四场 02/09 - 图13
101101

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  4. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  5. typedef long long ll;
  6. int main(){
  7. ll x;
  8. cin >> x;
  9. int n = log2(x) + 1;
  10. cout << (x << n) + x << endl;
  11. }

L

cnt[i][j] 表示前 i 个字符中 j 出现的个数。
cnt2[i][j][k] 表示前 i 个字符中,字符串 jk 作为子序列的出现个数。
设 (l,r,str) 表示区间 (l,r) 中 str 的出现次数:
由此,可以求出一个区间内字符 j 的个数。
也可以计算出一个区间字符 ab 的个数:
第四场 02/09 - 图14
然后,(l,r) 中字符串 abc 的出现次数可以分三部分来求解,
第四场 02/09 - 图15
然后现在问题简化成了求 abc 在 (1,r) 以及 (1,l-1) 这两个前缀中的出现位置。
由于有了 cnt2[i][j][k],如果用 d[a][b][c] 表示遍历过程中的前缀中子序列为 abc 的个数,那么遍历到 i 时:

  1. for a in (a..z):
  2. for b in (a..z):
  3. d[a][b][s[i]] += cnt2[i-1][a][b]

如此,便可计算。通过离散化操作累计答案即可。
复杂度 第四场 02/09 - 图16

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
  4. void err() { cout << "\033[39;0m" << endl; }
  5. template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
  6. #define rep(i,j,k) for(int i=int(j);i<=int(k);i++)
  7. #define per(i,j,k) for(int i=int(j);i>=int(k);i--)
  8. typedef long long ll;
  9. const int N = 80010, M = 500010;
  10. int n, m;
  11. char t[N];
  12. ll cnt[N][26], cnt2[N][26][26];
  13. ll get1(int l, int r, int c) {
  14. if(l > r) return 0;
  15. return cnt[r][c] - cnt[l - 1][c];
  16. }
  17. ll get2(int l, int r, int a, int b) {
  18. if(l > r) return 0;
  19. ll rs = cnt2[r][a][b] - cnt2[l-1][a][b];
  20. rs -= get1(1, l - 1, a) * get1(l, r, b);
  21. return rs;
  22. }
  23. struct Node {
  24. int a, b, c, id, type;
  25. };
  26. vector<Node> v[N];
  27. ll rs[M], d[26][26][26];
  28. int main(){
  29. scanf("%d%d", &n, &m);
  30. scanf("%s", t + 1);
  31. rep(i,1,n) {
  32. memcpy(cnt[i], cnt[i-1], sizeof cnt[i]);
  33. memcpy(cnt2[i], cnt2[i-1], sizeof cnt2[i]);
  34. int c = t[i] - 'a';
  35. cnt[i][c] ++;
  36. rep(j,0,25) {
  37. cnt2[i][j][c] += cnt[i-1][j];
  38. }
  39. }
  40. rep(i,1,m){
  41. char s[4];
  42. int l, r;
  43. scanf("%d%d%s", &l, &r, s);
  44. int a = s[0] - 'a';
  45. int b = s[1] - 'a';
  46. int c = s[2] - 'a';
  47. rs[i] -= get1(1, l-1, a) * get2(l, r, b, c) + get2(1, l-1, a, b) * get1(l, r, c);
  48. v[l-1].push_back({a, b, c, i, -1});
  49. v[r].push_back({a, b, c, i, 1});
  50. }
  51. rep(i,1,n) {
  52. int c = t[i] - 'a';
  53. rep(j,0,25) {
  54. rep(k,0,25) {
  55. d[j][k][c] += cnt2[i-1][j][k];
  56. }
  57. }
  58. for(auto &it : v[i]) {
  59. rs[it.id] += d[it.a][it.b][it.c] * it.type;
  60. }
  61. }
  62. rep(i,1,m) {
  63. printf("%lld\n", rs[i]);
  64. }
  65. }