[关闭]
@Dmaxiya 2020-12-15T23:32:10.000000Z 字数 11089 阅读 996

2018 Multi-University Training Contest 2

暑期集训


链接:2018 Multi-University Training Contest 2
过题数:3
排名:139/821
成员:官展鹏,冯彦博,孙昊哲

C. Cover

题意

在一个 个节点 条边的图中,用最少的一笔画把图上所有的边都覆盖一次,问最少要画多少划,并输出方案。

输入

多组输入(不超过 组),每组输入第一行为两个整数 ,接下去 行每行两个整数 ,表示在节点 之间有一条路径,数据保证没有重边与自环。

输出

对于每组输入,第一行输出一个整数 ,表示最少的笔画数,接下去 行,每行表示一条路径,每行的第一个数字 表示这条路径经过的边的数量,后面跟着 个整数,每个整数表示经过编号为 的边,如果经过第 条路径时是从 到达 (路径方向与输入相同),则输出 ,否则(路径方向与输入相反)输出 ,路径需要按照笔画顺序输出。如果有多组输出任意一组。

样例

输入
3 3
1 2
1 3
2 3
输出
1
3 1 3 -2

题解

对每个连通块分开处理,如果某个连通块内只有一个点,则不需要笔画,否则统计这个连通块内度为奇数的点的数量 ,则需要的最少笔画数量为 (欧拉路径性质),求和就是整张图的最少笔画数。然后对于每个连通块内度为奇数的点(点的个数一定为偶数),两两加边,这样就能使得所有点的度都为偶数,在新图上跑出欧拉路径,再将这个路径上之前加上去的边从环上删掉,就可以得到 条路径。

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cstring>
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <queue>
  11. #include <stack>
  12. #include <map>
  13. #include <set>
  14. #include <bitset>
  15. #include <algorithm>
  16. #include <sstream>
  17. using namespace std;
  18. #define LL long long
  19. const int maxn = 100000 + 100;
  20. struct Node {
  21. int pos, Index;
  22. Node() {}
  23. Node(int p, int I) {
  24. pos = p;
  25. Index = I;
  26. }
  27. };
  28. struct Edge {
  29. int u, v;
  30. };
  31. int n, m, u, v, cnt, mm, cnttmp, cas;
  32. int fa[maxn], num[maxn], deg[maxn], ans[maxn << 2], anstmp[maxn];
  33. vector<Node> G[maxn];
  34. vector<int> GG[maxn];
  35. Edge edge[maxn << 2];
  36. int vis[maxn << 2];
  37. bool cmp(const int &a, const int &b) {
  38. return deg[a] > deg[b];
  39. }
  40. void Init() {
  41. cnt = 0;
  42. mm = m + 1;
  43. for(int i = 1; i <= n; ++i) {
  44. fa[i] = i;
  45. num[i] = 0;
  46. deg[i] = 0;
  47. G[i].clear();
  48. GG[i].clear();
  49. }
  50. }
  51. int Find(int x) {
  52. return (x == fa[x]? x: fa[x] = Find(fa[x]));
  53. }
  54. void unit(int x, int y) {
  55. int xx = Find(x);
  56. int yy = Find(y);
  57. if(deg[xx] % 2 == 1) {
  58. swap(xx, yy);
  59. }
  60. if(xx != yy) {
  61. fa[xx] = yy;
  62. num[yy] += num[xx];
  63. }
  64. }
  65. void dfs(int x) {
  66. int len = G[x].size();
  67. for(int i = 0; i < len; ++i) {
  68. int pos = G[x][i].pos;
  69. int Index = G[x][i].Index;
  70. if(vis[abs(Index)] != cas) {
  71. vis[abs(Index)] = cas;
  72. dfs(pos);
  73. ans[cnt++] = Index;
  74. }
  75. }
  76. }
  77. int Next(int x, int mod) {
  78. return ((x - 1) % mod + mod) % mod;
  79. }
  80. void solve(int pos) {
  81. cnt = 0;
  82. sort(GG[pos].begin(), GG[pos].end(), cmp);
  83. int len = GG[pos].size();
  84. for(int i = 0; i < len; i += 2) {
  85. if(deg[GG[pos][i]] == 1) {
  86. u = GG[pos][i];
  87. v = GG[pos][i + 1];
  88. G[u].push_back(Node(v, mm));
  89. G[v].push_back(Node(u, -mm));
  90. ++mm;
  91. } else {
  92. break;
  93. }
  94. }
  95. dfs(pos);
  96. int End = -1;
  97. for(int i = 0; i < cnt; ++i) {
  98. if(abs(ans[i]) > m) {
  99. End = i;
  100. break;
  101. }
  102. }
  103. if(End == -1) {
  104. printf("%d", cnt);
  105. for(int i = cnt - 1; i >= 0; --i) {
  106. printf(" %d", ans[i]);
  107. }
  108. printf("\n");
  109. return ;
  110. }
  111. for(int i = Next(End, cnt); i != End; ) {
  112. cnttmp = 0;
  113. bool flag = false;
  114. for(int j = i; j != End; j = Next(j, cnt)) {
  115. if(abs(ans[j]) > m) {
  116. i = Next(j, cnt);
  117. flag = true;
  118. break;
  119. }
  120. anstmp[cnttmp++] = ans[j];
  121. }
  122. if(!flag) {
  123. i = End;
  124. }
  125. if(cnttmp == 0) {
  126. continue;
  127. }
  128. printf("%d", cnttmp);
  129. for(int j = 0; j < cnttmp; ++j) {
  130. printf(" %d", anstmp[j]);
  131. }
  132. printf("\n");
  133. }
  134. }
  135. int main() {
  136. #ifdef Dmaxiya
  137. freopen("test.txt", "r", stdin);
  138. #endif // Dmaxiya
  139. ios::sync_with_stdio(false);
  140. while(scanf("%d%d", &n, &m) != EOF) {
  141. ++cas;
  142. Init();
  143. for(int i = 1; i <= m; ++i) {
  144. scanf("%d%d", &u, &v);
  145. G[u].push_back(Node(v, i));
  146. G[v].push_back(Node(u, -i));
  147. ++deg[u];
  148. ++deg[v];
  149. edge[i].u = u;
  150. edge[i].v = v;
  151. }
  152. for(int i = 1; i <= n; ++i) {
  153. deg[i] %= 2;
  154. if(deg[i] == 1) {
  155. ++num[i];
  156. }
  157. }
  158. for(int i = 1; i <= m; ++i) {
  159. unit(edge[i].u, edge[i].v);
  160. }
  161. for(int i = 1; i <= n; ++i) {
  162. GG[Find(i)].push_back(i);
  163. }
  164. for(int i = 1; i <= n; ++i) {
  165. if((int)GG[i].size() <= 1) {
  166. continue;
  167. }
  168. cnt += max(num[i] / 2, 1);
  169. }
  170. printf("%d\n", cnt);
  171. for(int i = 1; i <= n; ++i) {
  172. if((int)GG[i].size() <= 1) {
  173. continue;
  174. }
  175. solve(i);
  176. }
  177. }
  178. return 0;
  179. }

D. Game

题意

两人玩一个博弈游戏,由 先开始,两人轮流进行下面的操作:在 个数字构成的集合中选择一个整数 ,然后将 的所有约数都从集合中删去,无法进行操作的一方失败。问如果双方都采取最优策略, 能否取得胜利。

输入

多组输入(不超过 组),每组输入包含一个整数

输出

对于每组输入,如果 可以获胜,则输出 ,否则输出

样例

输入
1
输出
Yes

题解

假设初始集合中只有 个整数,若 选择了数字 可以获胜,那么对于 的集合选择 之后剩下的数字与初始集合为 的下一个状态完全相同,都是必败态,若 初始集合 是一种必败态,那么 只要选择数字 就可以将必败态转移给 ,所以无论如何 都将获胜。

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cfloat>
  7. #include <cstring>
  8. #include <string>
  9. #include <vector>
  10. #include <list>
  11. #include <queue>
  12. #include <stack>
  13. #include <map>
  14. #include <set>
  15. #include <algorithm>
  16. using namespace std;
  17. #define LL long long
  18. int n;
  19. int main() {
  20. #ifdef Dmaxiya
  21. freopen("test.txt", "r", stdin);
  22. #endif // Dmaxiya
  23. ios::sync_with_stdio(false);
  24. while(scanf("%d", &n) != EOF) {
  25. printf("Yes\n");
  26. }
  27. return 0;
  28. }

E. Hack It

题意

构造一个 矩阵,要求矩阵内的 的个数不小于 且对于矩阵内任意一个子矩阵,子矩阵中的四个角的数字不同时为

输入

无。

输出

第一行为一个整数 ,表示将要输出的矩阵大小,接下来 列的字符串,字符串内每个字符只能为 或者

样例

输入
输出
3
010
000
000
提示
显然这个输出并不合法,只是为了说明输出格式。

题解

关于构造方法的证明思路,不会,详见大佬给出的证明(也可以直接跳过证明看构造的方法):


文字描述构造方案比较麻烦,直接看图找规律比较好些,构造方案如下( 的情况下):


于是对于 的情况,找到子矩阵大小为 的就可以满足条件,将多出 的部分截断即可。

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cstring>
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <queue>
  11. #include <stack>
  12. #include <map>
  13. #include <set>
  14. #include <bitset>
  15. #include <algorithm>
  16. #include <sstream>
  17. using namespace std;
  18. #define LL long long
  19. const int n = 47;
  20. const int maxn = n * n + 100;
  21. char str[maxn][maxn];
  22. int main() {
  23. #ifdef Dmaxiya
  24. freopen("test.txt", "r", stdin);
  25. #endif // Dmaxiya
  26. ios::sync_with_stdio(false);
  27. memset(str, '0', sizeof(str));
  28. for(int i = 0; i < n; ++i) {
  29. for(int j = 0; j < n; ++j) {
  30. str[i][j * n + (i * j) % n] = '1';
  31. }
  32. }
  33. for(int i = 1; i < n; ++i) {
  34. for(int j = 0; j < n; ++j) {
  35. for(int k = 0; k < n; ++k) {
  36. for(int l = 0; l < n; ++l) {
  37. str[i * n + j][k * n + l] = str[(i - 1) * n + j][k * n + ((l - 1) % n + n) % n];
  38. }
  39. }
  40. }
  41. }
  42. printf("2000\n");
  43. for(int i = 0; i < 2000; ++i) {
  44. str[i][2000] = '\0';
  45. printf("%s\n", str[i]);
  46. }
  47. return 0;
  48. }

F. Matrix

题意

对于一个 的网格,每一个小格只能涂上黑色或者白色,问所有涂色方案中,至少有 列为黑色的方案数。

输入

多组输入(不超过 组),每组为四个整数

输出

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

样例

输入
3 4 1 2
输出
169

题解

根据容斥原理可以知道,至少 列为黑色的方案数为 ,如果 是从 开始的,那么 的值就为 ,然而 不是从 开始的,而是从某个数字开始的,于是该系数就为 。系数 具体的推导可以根据在 容斥的加减计算过程中,至少为 行的状态被重复计算的次数来得到。最后对 进行 次预处理,再 地计算容斥公式,就可以得到答案。

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cstring>
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <queue>
  11. #include <stack>
  12. #include <map>
  13. #include <set>
  14. #include <bitset>
  15. #include <algorithm>
  16. #include <sstream>
  17. using namespace std;
  18. #define LL long long
  19. const int maxn = 3001;
  20. const int MOD = 998244353;
  21. int n, m, A, B, ans;
  22. int C[maxn][maxn], two[maxn * maxn];
  23. int fa[maxn], fb[maxn];
  24. void Init() {
  25. for(int i = 0; i < maxn; ++i) {
  26. for(int j = 0; j <= i; ++j) {
  27. if(j == i || j == 0) {
  28. C[i][j] = 1;
  29. } else {
  30. C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
  31. if(C[i][j] >= MOD) {
  32. C[i][j] -= MOD;
  33. }
  34. }
  35. }
  36. }
  37. two[0] = 1;
  38. for(int i = 1; i < maxn * maxn; ++i) {
  39. two[i] = two[i - 1] * 2;
  40. if(two[i] >= MOD) {
  41. two[i] -= MOD;
  42. }
  43. }
  44. }
  45. int main() {
  46. #ifdef Dmaxiya
  47. freopen("test.txt", "r", stdin);
  48. #endif // Dmaxiya
  49. ios::sync_with_stdio(false);
  50. Init();
  51. while(scanf("%d%d%d%d", &n, &m, &A, &B) != EOF) {
  52. ans = 0;
  53. for(int i = A; i <= n; ++i) {
  54. fa[i] = 0;
  55. for(int j = A; j < i; ++j) {
  56. fa[i] = (fa[i] + (LL)C[i][j] * fa[j]) % MOD;
  57. }
  58. fa[i] = 1 - fa[i];
  59. if(fa[i] < 0) {
  60. fa[i] += MOD;
  61. }
  62. }
  63. for(int i = B; i <= m; ++i) {
  64. fb[i] = 0;
  65. for(int j = B; j < i; ++j) {
  66. fb[i] = (fb[i] + (LL)C[i][j] * fb[j]) % MOD;
  67. }
  68. fb[i] = 1 - fb[i];
  69. if(fb[i] < 0) {
  70. fb[i] += MOD;
  71. }
  72. }
  73. for(int i = A; i <= n; ++i) {
  74. LL tmp = (LL)fa[i] * C[n][i] % MOD;
  75. for(int j = B; j <= m; ++j) {
  76. ans = (ans + ((tmp * fb[j] % MOD) * C[m][j] % MOD) * two[(n - i) * (m - j)] % MOD) % MOD;
  77. }
  78. }
  79. printf("%d\n", ans);
  80. }
  81. return 0;
  82. }

G. Naive Operations

题意

有两个长度为 的序列 是一个 的排列, 序列初始每个值都为 ,有 次操作,每次操作为以下两种之一:

  1. :将区间 内的所有数字都加上
  2. :询问 的值。

输入

多组输入(不超过 组),每组输入第一行包含两个整数 ,接下去一行为 个整数 ,序列 的一个排列,接下去 行每行为一个字符串和两个区间的端点 ,若 ,则执行操作 ,否则执行操作

输出

对于每组询问,输出询问的答案。

样例

输入
5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5
输出
1
1
2
4
4
6

题解

由于每次操作都只对区间 ,最坏情况下,如果每次操作都对整个区间 ,那么 次操作后区间的和为 ,因此对于每次 增加 时都更新一次(用树状数组更新复杂度为 ),这样区间更新就用区间增减区间最小值的线段树维护,线段树上每个数初始为 ,每次操作 就将区间 内的数字都 ,每当区间最小值为 时就单点更新这个最小值,并将这个值赋值为 ,总的时间复杂度约为

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cstring>
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <queue>
  11. #include <stack>
  12. #include <map>
  13. #include <set>
  14. #include <bitset>
  15. #include <algorithm>
  16. #include <sstream>
  17. using namespace std;
  18. #define LL long long
  19. const int maxn = 100000 + 100;
  20. int n, q, x, y;
  21. char str[20];
  22. int num[maxn];
  23. int Min[maxn << 2], lazy[maxn << 2];
  24. int sum[maxn];
  25. int lowbit(int x) {
  26. return x & (-x);
  27. }
  28. void update(int Index, int x) {
  29. while(Index <= n) {
  30. sum[Index] += x;
  31. Index += lowbit(Index);
  32. }
  33. }
  34. int query(int Index) {
  35. int ret = 0;
  36. while(Index > 0) {
  37. ret += sum[Index];
  38. Index -= lowbit(Index);
  39. }
  40. return ret;
  41. }
  42. int query(int l, int r) {
  43. return query(r) - query(l - 1);
  44. }
  45. void push_up(int rt) {
  46. Min[rt] = min(Min[rt << 1], Min[rt << 1 | 1]);
  47. }
  48. void push_down(int rt) {
  49. if(lazy[rt] != 0) {
  50. lazy[rt << 1] += lazy[rt];
  51. lazy[rt << 1 | 1] += lazy[rt];
  52. Min[rt << 1] += lazy[rt];
  53. Min[rt << 1 | 1] += lazy[rt];
  54. lazy[rt] = 0;
  55. }
  56. }
  57. void build(int l, int r, int rt) {
  58. lazy[rt] = 0;
  59. if(l == r) {
  60. sum[l] = 0;
  61. Min[rt] = num[l];
  62. return ;
  63. }
  64. int m = (l + r) >> 1;
  65. build(l, m, rt << 1);
  66. build(m + 1, r, rt << 1 | 1);
  67. push_up(rt);
  68. }
  69. void solve(int l, int r, int rt) {
  70. if(l == r) {
  71. update(l, 1);
  72. Min[rt] = num[l];
  73. return ;
  74. }
  75. push_down(rt);
  76. int m = (l + r) >> 1;
  77. if(Min[rt << 1] == 0) {
  78. solve(l, m, rt << 1);
  79. }
  80. if(Min[rt << 1 | 1] == 0) {
  81. solve(m + 1, r, rt << 1 | 1);
  82. }
  83. push_up(rt);
  84. }
  85. void update(int L, int R, int l, int r, int rt) {
  86. if(L <= l && r <= R) {
  87. --Min[rt];
  88. --lazy[rt];
  89. if(Min[rt] == 0) {
  90. solve(l, r, rt);
  91. }
  92. return ;
  93. }
  94. push_down(rt);
  95. int m = (l + r) >> 1;
  96. if(L <= m) {
  97. update(L, R, l, m, rt << 1);
  98. }
  99. if(m < R) {
  100. update(L, R, m + 1, r, rt << 1 | 1);
  101. }
  102. push_up(rt);
  103. }
  104. int main() {
  105. #ifdef Dmaxiya
  106. freopen("test.txt", "r", stdin);
  107. #endif // Dmaxiya
  108. ios::sync_with_stdio(false);
  109. while(scanf("%d%d", &n, &q) != EOF) {
  110. for(int i = 1; i <= n; ++i) {
  111. scanf("%d", &num[i]);
  112. }
  113. build(1, n, 1);
  114. for(int i = 0; i < q; ++i) {
  115. scanf("%s%d%d", str, &x, &y);
  116. if(str[0] == 'a') {
  117. update(x, y, 1, n, 1);
  118. } else {
  119. printf("%d\n", query(x, y));
  120. }
  121. }
  122. }
  123. return 0;
  124. }

J. Swaps and Inversions

题意

对于一个长度为 的序列,如果序列中存在一个逆序对,则需要花费 的代价,为了减少代价,可以先将任意个相邻的数字进行交换,每次交换需要花费 的代价,问总的最小代价为多少?

输入

组输入,每组输入的第一行为三个整数 ,第二行为 个整数

输出

输出最小代价。

样例

输入
3 233 666
1 2 3
3 1 666
3 2 1
输出
0
3

题解

由于每次代价为 的操作最多能减少一个逆序对,所以代价为 的操作次数与最终序列中剩下的逆序对的数量和,等于初始序列的逆序对数,因此答案就是总的逆序对数乘上 。对整个序列离散化然后用树状数组求逆序对。

过题代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <climits>
  6. #include <cstring>
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <queue>
  11. #include <stack>
  12. #include <map>
  13. #include <set>
  14. #include <bitset>
  15. #include <algorithm>
  16. using namespace std;
  17. #define LL long long
  18. const int maxn = 100000 + 100;
  19. struct Node {
  20. int num, Index;
  21. };
  22. bool operator<(const Node &a, const Node &b) {
  23. return a.num < b.num;
  24. }
  25. int n, cnt;
  26. Node node[maxn];
  27. LL x, y, ans;
  28. int num[maxn], sum[maxn];
  29. int lowbit(int x) {
  30. return (x & (-x));
  31. }
  32. void update(int Index, LL x) {
  33. while(Index <= n) {
  34. sum[Index] += x;
  35. Index += lowbit(Index);
  36. }
  37. }
  38. int query(int Index) {
  39. LL ret = 0;
  40. while(Index > 0) {
  41. ret += sum[Index];
  42. Index -= lowbit(Index);
  43. }
  44. return ret;
  45. }
  46. int query(int l, int r) {
  47. return query(r) - query(l - 1);
  48. }
  49. int main() {
  50. #ifdef Dmaxiya
  51. freopen("test.txt", "r", stdin);
  52. #endif // Dmaxiya
  53. while(scanf("%d%I64d%I64d", &n, &x, &y) != EOF) {
  54. ans = 0;
  55. for(int i = 1; i <= n; ++i) {
  56. scanf("%d", &num[i]);
  57. node[i].num = num[i];
  58. node[i].Index = i;
  59. sum[i] = 0;
  60. }
  61. sort(node + 1, node + n + 1);
  62. cnt = 1;
  63. num[node[1].Index] = cnt;
  64. for(int i = 2; i <= n; ++i) {
  65. if(node[i].num != node[i - 1].num) {
  66. ++cnt;
  67. }
  68. num[node[i].Index] = cnt;
  69. }
  70. for(int i = 1; i <= n; ++i) {
  71. ans += query(num[i] + 1, n);
  72. update(num[i], 1);
  73. }
  74. printf("%I64d\n", min(x, y) * ans);
  75. }
  76. return 0;
  77. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注