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

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. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注