[关闭]
@Dmaxiya 2026-03-09T07:04:53.000000Z 字数 7593 阅读 10

2018 Multi-University Training Contest 8

暑期集训


链接:2018 Multi-University Training Contest 8
过题数:3
排名:243
成员:孙昊哲、官展鹏、葛沛鑫

A. Character Encoding

题意

个在 范围内的整数,问有多少种方案使得 个整数的和等于 。其中, 属于两种不同的方案。

输入

第一行一个整数 ,表示有 组数据,接下去 行,每行 个整数

数据保证所有 各自的和不超过

输出

输出方案数对 取余的结果。

题解

若对每个整数的取值无 大小的限制,则 个自然数的和为 的方案数为 (隔板法),若至少有 个整数的取值大于等于 ,相当于取 个自然数的和为 ,方案数为 ,同理若至少有 个整数的取值大于等于 ,则方案数为

由容斥原理可得:所有 个整数都在 范围内的方案数等于:所有数字无最大值限制的方案数减去至少有 个整数大于等于 的方案数,再加上至少有 个整数大于等于 的方案数,再减去……直到最后加上(或者减去)至少有 个整数大于等于 的方案数,公式如下:

过题代码

  1. #include <cstdio>
  2. using namespace std;
  3. typedef long long LL;
  4. const LL MOD = 998244353;
  5. const int maxn = 200000 + 100;
  6. LL T, n, m, k;
  7. LL ans, flag;
  8. LL inv[maxn], pro[maxn], invpro[maxn];
  9. void Prepare_C() {
  10. inv[1] = 1;
  11. for (int i = 2; i < maxn; ++i) {
  12. inv[i] = (LL)(MOD - MOD / i) * inv[MOD % i] % MOD;
  13. }
  14. pro[0] = invpro[0] = 1;
  15. for (int i = 1; i < maxn; ++i) {
  16. pro[i] = pro[i - 1] * i % MOD;
  17. invpro[i] = invpro[i - 1] * inv[i] % MOD;
  18. }
  19. }
  20. LL get_C(LL n, LL m) {
  21. if(n < m) {
  22. return 0;
  23. }
  24. return pro[n] * invpro[m] % MOD * invpro[n - m] % MOD;
  25. }
  26. int main() {
  27. #ifdef ExRoc
  28. freopen("test.txt", "r", stdin);
  29. #endif // ExRoc
  30. Prepare_C();
  31. scanf("%I64d", &T);
  32. while (T--) {
  33. scanf("%I64d%I64d%I64d", &n, &m, &k);
  34. ans = get_C(k + m - 1, m - 1);
  35. flag = 1;
  36. for (int i = 1; i <= m; ++i) {
  37. flag *= -1;
  38. ans = ((ans + get_C(k - i * n + m - 1, m - 1) * get_C(m, i) % MOD * flag) % MOD + MOD) % MOD;
  39. }
  40. printf("%I64d\n", ans);
  41. }
  42. return 0;
  43. }

D. Parentheses Matrix

题意

构造一个 列的只由小括号组成的矩阵,要求合法括号的行数与列数和最大。如下括号矩阵的第 行与第 列属于合法的括号匹配:

  1. )()(
  2. ()()

输入

第一行一个整数 ,表示有 组数据,接下去 行,每行 个整数

输出

按题意输出小括号矩阵,若有多个满足条件的矩阵,输出任意一个即可。

题解

均为奇数,则无法构成任何一行(列)合法括号匹配。

只有一个为偶数,则只能构造出奇数行(列)合法括号匹配。

讨论 均为偶数的情况(假设 ):

  • ,则每列一个合法括号匹配可满足条件;
  • ,则按如下方式可构造出最多 组合法括号序列:
  1. ((((((((((
  2. ((((()))))
  3. )))))(((((
  4. ))))))))))
  • ,则按如下方式可构造出最多 组合法括号序列:
  1. ((((((((((
  2. ()()()()()
  3. (()()()())
  4. ()()()()()
  5. (()()()())
  6. ))))))))))

过题代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 200 + 100;
  5. int T, n, m;
  6. char str[maxn][maxn];
  7. void construct(int n, int m) {
  8. for (int i = 0; i < n / 2; ++i) {
  9. for (int j = 0; j < m; ++j) {
  10. if (j < m / 2) {
  11. str[i][j] = '(';
  12. } else {
  13. str[i][j] = ')';
  14. }
  15. }
  16. }
  17. for (int i = n / 2; i < n; ++i) {
  18. for (int j = 0; j < m; ++j) {
  19. if (j % 2 == 0) {
  20. str[i][j] = '(';
  21. } else {
  22. str[i][j] = ')';
  23. }
  24. }
  25. }
  26. }
  27. void construct2(int n, int m) {
  28. for (int j = 0; j < m; ++j) {
  29. str[0][j] = '(';
  30. str[n - 1][j] = ')';
  31. }
  32. for (int i = 1; i < n - 1; ++i) {
  33. for (int j = 0; j < m; ++j) {
  34. if (j == 0) {
  35. str[i][j] = '(';
  36. } else if (j == m - 1) {
  37. str[i][j] = ')';
  38. } else if ((i + j) % 2 == 0) {
  39. str[i][j] = ')';
  40. } else {
  41. str[i][j] = '(';
  42. }
  43. }
  44. }
  45. }
  46. void print(bool flag) {
  47. for (int i = 0; i < n; ++i) {
  48. for (int j = 0; j < m; ++j) {
  49. if (!flag) {
  50. printf("%c", str[i][j]);
  51. } else {
  52. printf("%c", str[j][i]);
  53. }
  54. }
  55. printf("\n");
  56. }
  57. printf("\n");
  58. }
  59. int main() {
  60. #ifdef ExRoc
  61. freopen("test.txt", "r", stdin);
  62. #endif // ExRoc
  63. cin >> T;
  64. while (T--) {
  65. cin >> n >> m;
  66. if (n % 2 == 1) {
  67. construct(n, m);
  68. print(false);
  69. } else {
  70. if (m % 2 == 1) {
  71. construct(m, n);
  72. print(true);
  73. } else {
  74. if (n > m) {
  75. if (m <= 4) {
  76. construct(n, m);
  77. print(false);
  78. } else {
  79. construct2(m, n);
  80. print(true);
  81. }
  82. } else {
  83. if (n <= 4) {
  84. construct(m, n);
  85. print(true);
  86. } else {
  87. construct2(n, m);
  88. print(false);
  89. }
  90. }
  91. }
  92. }
  93. }
  94. return 0;
  95. }

E. Magic Square

题意

给定一个 的数字矩阵,矩阵的每一位为 个互不相等的数字,该矩阵可以分为 的小矩阵,分别用 表示,给出 条指令,每条指令包含两部分:小矩阵对应的编号以及 C 或者 R,表示将对应的小矩阵进行逆时针或者顺时针旋转。

输出初始矩阵经过 次操作后的最终状态。

输入

第一行一个整数 ,表示有 组数据。每组数据的第一行为一个整数 ,表示有 条指令,后面跟着一个 的数字矩阵,接下去 行每行 个字符,第一个字符为一个数字,表示小矩阵编号,第二个字符为 C 或者 R,表示指令的旋转方向。

输出

输出 矩阵的最终状态。

题解

按题意模拟。

过题代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 100;
  5. int T, n;
  6. char cmd[maxn];
  7. char str[maxn][maxn];
  8. int swp[2][3][2] = {
  9. {{0, 1}, {1, 1}, {1, 0}},
  10. {{1, 0}, {1, 1}, {0, 1}}
  11. };
  12. int main() {
  13. #ifdef ExRoc
  14. freopen("test.txt", "r", stdin);
  15. #endif // ExRoc
  16. cin >> T;
  17. while (T--) {
  18. cin >> n;
  19. for (int i = 0; i < 3; ++i) {
  20. cin >> str[i];
  21. }
  22. for (int i = 0; i < n; ++i) {
  23. cin >> cmd;
  24. int x = (cmd[0] - '0' - 1) / 2;
  25. int y = (cmd[0] - '0' - 1) % 2;
  26. int idx = cmd[1] == 'R';
  27. for (int i = 0; i < 3; ++i) {
  28. swap(str[x][y], str[x + swp[idx][i][0]][y + swp[idx][i][1]]);
  29. }
  30. }
  31. for (int i = 0; i < 3; ++i) {
  32. cout << str[i] << endl;
  33. }
  34. }
  35. return 0;
  36. }

J. Taotao Picks Apples

题意

个苹果,高度分别为 ,Taotao 只会从 依次取苹果,取苹果的条件为:当前为第一个苹果,或者当前苹果的高度高于上一次取到的苹果。有 次独立的询问,每次将第 个苹果的高度改为 ,问在修改高度后,按规则 Taotao 会取到多少个苹果。

输入

第一行一个整数 ,表示有 组数据,每组数据第一行为两个整数 ,第二行 个整数 ,接下去 行每行两个整数 ,含义如题。

输出

对于每次询问,输出一行整数,表示 Taotao 能取到的苹果数。

题解

预处理前缀数组 表示从第 个到底 个苹果高度都不修改的情况下按规则能取到的苹果数。

若每次能快速查询第 个苹果高度修改为 时,从第 位往后(不考虑前缀情况)按规则能取到多少个苹果,这里需要维护一个单调递增的“当前后缀取苹果数”来支持快速的二分查询。可以发现若按下标从大到小询问,就能用单调栈维护这个递增序列。

  • 大于 (即前缀最大值),则答案为 (其中 为查询到的后缀合法取苹果数);
  • 小于等于 ,则令 后,再查询 ,答案为

过题代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 100000 + 100;
  5. struct Node {
  6. int q, idx;
  7. Node() {}
  8. Node(int q, int idx) : q(q), idx(idx) {}
  9. };
  10. int T, n, m, p, q, last, top;
  11. int num[maxn], pre[maxn], sta[maxn], ans[maxn], preMax[maxn];
  12. vector<Node> vct[maxn];
  13. int getAns(int idx, int x) {
  14. int low = -1;
  15. int high = top;
  16. int mid;
  17. while (high - low > 1) {
  18. mid = (high + low) >> 1;
  19. if (sta[mid] > x) {
  20. low = mid;
  21. } else {
  22. high = mid;
  23. }
  24. }
  25. return pre[idx - 1] + 1 + low + 1;
  26. }
  27. int main() {
  28. #ifdef ExRoc
  29. freopen("test.txt", "r", stdin);
  30. #endif
  31. scanf("%d", &T);
  32. while (T--) {
  33. scanf("%d%d", &n, &m);
  34. last = 0;
  35. for (int i = 1; i <= n; ++i) {
  36. scanf("%d", &num[i]);
  37. if (num[i] > last) {
  38. last = num[i];
  39. pre[i] = pre[i - 1] + 1;
  40. } else {
  41. pre[i] = pre[i - 1];
  42. }
  43. vct[i].clear();
  44. preMax[i] = max(preMax[i - 1], num[i]);
  45. }
  46. for (int i = 0; i < m; ++i) {
  47. scanf("%d%d", &p, &q);
  48. vct[p].push_back(Node(q, i));
  49. }
  50. top = 0;
  51. for (int i = n; i >= 1; --i) {
  52. int len = vct[i].size();
  53. for (int j = 0; j < len; ++j) {
  54. q = vct[i][j].q;
  55. int idx = vct[i][j].idx;
  56. if (q <= preMax[i - 1]) {
  57. ans[idx] = getAns(i, preMax[i - 1]) - 1;
  58. } else {
  59. ans[idx] = getAns(i, vct[i][j].q);
  60. }
  61. }
  62. while (top != 0 && sta[top - 1] <= num[i]) {
  63. --top;
  64. }
  65. sta[top++] = num[i];
  66. }
  67. for (int i = 0; i < m; ++i) {
  68. printf("%d\n", ans[i]);
  69. }
  70. }
  71. return 0;
  72. }

K. Pop the Balloons

题意

你有一把强力的枪,在打中一个 列的气球矩阵中第 行第 列的气球时,与该气球同一行与同一列的所有气球都会被“风”吹爆,你每次必须打一个仍存在场上的气球,没有气球的位置不能打,问只打 枪的情况下,有多少种可能的射击方式使得所有气球都被打爆,对 范围内所有值输出一个答案。

输入

第一行一个整数 ,表示有 组数据,每组数据第一行为三个整数 ,含义如题,接下去 行每行 个字符,字符只可能为 . 或者 Q,为 . 表示这个位置没有气球,为 Q 表示这个位置有一个气球。

输出

每组数据输出 行整数,每个整数对应一次题目询问的答案。

题解

表示到第 列时,状态为 的方案数(由于行数小于列数,所以以列为第一维状态数更少),状态用 进制数表示:

  • 的第 位(这里的 与题目输入的 无关)为 时,表示第 行从未出现过气球,也不需要射击;
  • 时,表示第 行出现过气球,但未射击过;
  • 时,表示第 行出现过气球,且已射击过。

在遍历列时,可以枚举转移两种状态:

  • 该列不射击,则转移前一列原状态,加上之前未射击过,但该列中有气球的行,将这些行对应的状态置
  • 射击该列的第 行,则转移前一列原状态,并将第 位置 ,其他之前未射击过,但该列中有气球的行,将这些行对应的状态置

最后加上一些状态转移优化,以及答案爆 long long,可通过该题。

过题代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const LL Limit = 1000000000000LL;
  5. LL T, n, m, k;
  6. char str[12][50];
  7. LL bit[531442][12], twoCnt[531442];
  8. LL dp[21][531442], ans[21], pow3[21], fac[50];
  9. int getBit(int x, int i) {
  10. return x / pow3[i] % 3;
  11. }
  12. void init() {
  13. fac[0] = 1;
  14. pow3[0] = 1;
  15. for (int i = 1; i <= 20; ++i) {
  16. pow3[i] = pow3[i - 1] * 3;
  17. fac[i] = fac[i - 1] * i;
  18. }
  19. for (int i = 0; i < 531442; ++i) {
  20. int cnt = 0;
  21. for (int j = 0; j < 12; ++j) {
  22. bit[i][j] = getBit(i, j);
  23. if (bit[i][j] == 2) {
  24. ++cnt;
  25. }
  26. }
  27. twoCnt[i] = cnt;
  28. }
  29. }
  30. int getBit2(int x, int i) {
  31. return bit[x][i];
  32. }
  33. int setBit(int x, int i, int y) {
  34. x -= getBit2(x, i) * pow3[i];
  35. x += y * pow3[i];
  36. return x;
  37. }
  38. string toString(int x) {
  39. string str;
  40. for (int i = 0; i < n; ++i) {
  41. str += (char)('0' + getBit2(x, i));
  42. }
  43. return str;
  44. }
  45. int main() {
  46. ios::sync_with_stdio(false);
  47. init();
  48. scanf("%I64d", &T);
  49. while (T--) {
  50. scanf("%I64d%I64d%I64d", &n, &m, &k);
  51. for (int i = 0; i < n; ++i) {
  52. scanf("%s", str[i]);
  53. }
  54. dp[0][0] = 1;
  55. for (int i = 1; i <= m; ++i) {
  56. memset(dp[i], 0, sizeof(LL) * pow3[n]);
  57. }
  58. for (int i = 0; i < m; ++i) {
  59. int now = i + 1;
  60. int pre = i;
  61. for (int j = 0; j < pow3[n]; ++j) {
  62. if (dp[pre][j] == 0 || twoCnt[j] > k) {
  63. continue;
  64. }
  65. int jj = j;
  66. for (int k = 0; k < n; ++k) {
  67. if (str[k][i] == 'Q' && getBit2(jj, k) != 2) {
  68. jj = setBit(jj, k, 1);
  69. dp[now][setBit(j, k, 2)] += dp[pre][j];
  70. }
  71. }
  72. dp[now][jj] += dp[pre][j];
  73. }
  74. }
  75. memset(ans, 0, sizeof(ans));
  76. for (int i = 0; i < pow3[n]; ++i) {
  77. bool flag = true;
  78. int cnt = 0;
  79. for (int j = 0; j < n; ++j) {
  80. if (getBit2(i, j) == 1) {
  81. flag = false;
  82. }
  83. if (getBit2(i, j) == 2) {
  84. ++cnt;
  85. }
  86. }
  87. if (flag) {
  88. ans[cnt] += dp[m][i];
  89. }
  90. }
  91. for (int i = 1; i <= k; ++i) {
  92. __int128 Ans = (__int128)ans[i] * fac[i];
  93. if (Ans > Limit) {
  94. printf("%I64d%012I64d\n", (LL)(Ans / Limit), (LL)(Ans % Limit));
  95. } else {
  96. printf("%I64d\n", (LL)Ans);
  97. }
  98. }
  99. }
  100. return 0;
  101. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注