[关闭]
@dxbdly 2022-10-09T15:23:35.000000Z 字数 5629 阅读 173

10.6模拟赛 —— Nescafe17

2022秋


Contest_Summary

Expect:

T1 T2 T3 Sum
100 100 40 240

Present:

T1 T2 T3 Sum Rank
30 100 400 170 5

T1 Magician

Description

给你 个点,现在依次加入 条边,每加入一条边,你都需要求出当前的图有多少非空子图是欧拉回路森林。

欧拉回路森林指每个点的度数均为非零偶数的图。

Solution

首先很容易知道答案单调,观察样例,发现要么不变,要么

考虑变化的条件,不难发现如果新增的边两端点联通,则答案增加。

考虑感性证明一下,可以把这张图分成很多个小环,那么新增一条边,如果两端点联通,则一定生成唯一一个小环。

所以答案会增加 倍,并且还要加上新生成的这个环,所以会

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. #define int long long
  4. using namespace std;
  5. inline int read() {
  6. int x = 0;
  7. char c = getchar();
  8. bool f = 0;
  9. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  10. while(isdigit(c)) x = (x * 10) + (c ^ 48),c = getchar();
  11. return f ? -x : x;
  12. }
  13. const int maxn = 2 * 1e5 + 5, mod = 1e9 + 9;
  14. int n, m;
  15. int Father[maxn], Answer = 0;
  16. inline int Find(int x) {
  17. if(Father[x] != x) return Father[x] = Find(Father[x]);
  18. return Father[x];
  19. }
  20. inline void Unnion(int u, int v) {
  21. int f1 = Find(u), f2 = Find(v);
  22. if(f1 != f2) Father[f2] = f1;
  23. }
  24. signed main() {
  25. // freopen("magician.in", "r", stdin);
  26. // freopen("magician.out", "w", stdout);
  27. n = read(), m = read();
  28. for(register int i = 1; i <= n; ++i) Father[i] = i;
  29. for(register int i = 1; i <= m; ++i) {
  30. int u = read(), v = read();
  31. if(Find(u) != Find(v)) printf("%lld\n", Answer % mod);
  32. else Answer = (Answer << 1ll | 1ll) % mod, printf("%lld\n", Answer % mod);
  33. Unnion(u, v);
  34. }
  35. return 0;
  36. }

Summary

无论是猜到结论还是证明都不是很难,但是出现细节性读题失误。

T2 Guard

Description

给你 个物品,每个物品有两个属性
你最开始的得分为 ,你有 的概率拿走第 个物品,并且你的得分会加上

求你最终拿到至少 个物品,并且得分为非负数的概率,保留六位小数。

Solution

首先考虑背包,特殊点在于 为负时最多为

所以容量超过 时多余的就没有意义了,所以可以将所有容量与

然后在多开一维记录赢了几场即可。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. #define f(i, j, k) f[i][min(j, n) + 201][k]
  4. using namespace std;
  5. inline int read() {
  6. int x = 0;
  7. char c = getchar();
  8. bool f = 0;
  9. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  10. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  11. return f ? -x : x;
  12. }
  13. const int maxn = 205;
  14. int n, L, K;
  15. int Val[maxn];
  16. double p[maxn], f[2][maxn << 1][maxn], Answer;
  17. int main() {
  18. // freopen("guard.in", "r", stdin);
  19. // freopen("guard.out", "w", stdout);
  20. n = read(), L = read(), K = read();
  21. for(register int i = 1; i <= n; ++i) {
  22. int x = read();
  23. p[i] = (double) x / 100.0;
  24. }
  25. for(register int i = 1; i <= n; ++i) Val[i] = read();
  26. f(0, K, 0) = 1.0;
  27. for(register int i = 0; i < n; ++i) {
  28. for(register int j = -1 * i; j <= n; ++j)
  29. for(register int k = 0; k <= i; ++k) f((i & 1) ^ 1, j, k) = 0;
  30. for(register int j = -1 * i; j <= n; ++j)
  31. for(register int k = 0; k <= i; ++k) {
  32. f((i & 1) ^ 1, j + Val[i + 1], k + 1) += p[i + 1] * f(i & 1, j, k);
  33. f((i & 1) ^ 1, j, k) += (1.0 - p[i + 1]) * f(i & 1, j, k);
  34. }
  35. }
  36. for(register int i = 0; i <= n; ++i)
  37. for(register int j = L; j <= n; ++j) Answer += f(n & 1, i, j);
  38. printf("%0.6lf", Answer);
  39. return 0;
  40. }

Summary

空间差点炸掉,考场上贪了不想滚动,其实无论是滚掉一维还是把数组排序,都能使空间保证不会炸。

T3 Laser

Description

给你 个区间,对于数字 中的两个不同的数 ,若在这 个区间中任意选择一个数 ,将其低 位中的某一位的 换成 (或者 换成 )均都能落在这 个区间内,则称 的。

若一个集合 内的元素都是互相 的,则称集合

按照元素的最小值排序所有 后输出。

Solution

一道很好的题,类似数位 DP ?

首先要明确一件事,对于某个区间,比较其两端点的每一位,都会对答案造成限制,我们要做的就是如何很好的 check 对于当前位到底哪些数间有限制。

首先可以做一个预处理,把所有形如 的区间合并,这样在不同的区间间一定有一些空格。

对于当前的数位 分类讨论。

在这中间可以再分两类。

:

已经确定为 ,只需要枚举

此时新区间为

考虑如何 check 此区间是否合法,只需要二分这两个端点,是否在原图中的同一个区间,如果不在则必定不合法,因为此时一定有一个数会落到空格中。

到此这种情况解决完毕。

此时 有三种取值情况,每种情况对应了一种不同的可行区间,如果更改后的区间落在此区间则合法。

此种情况考虑完毕。

考虑前缀取 的情况,发现第 位如何改都没问题,所以不需要考虑这段区间。

所以只需考虑前缀都等于 即可,此时前缀相等,递归到第一大类求解。

至此所有情况就考虑完备了。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. #define int long long
  4. using namespace std;
  5. inline int read() {
  6. int x = 0;
  7. char c = getchar();
  8. bool f = 0;
  9. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  10. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  11. return f ? -x : x;
  12. }
  13. const int maxn = 2e4 + 5;
  14. int n, K;
  15. struct node {
  16. int l, r;
  17. }Line[maxn];
  18. int Vst[15][15], Use[15];
  19. inline int Get_Id(int x) {
  20. int l = 1, r = n;
  21. while(l < r) {
  22. int mid = (l + r) >> 1;
  23. if(x <= Line[mid].r) r = mid;
  24. else l = mid + 1;
  25. }
  26. return Line[l].l <= x && x <= Line[l].r ? l : 0;
  27. }
  28. inline void Calc(int x, int y, int j, int k) {
  29. x = Get_Id(x), y = Get_Id(y);
  30. Vst[j][k] = Vst[k][j] = (x && y && x == y);
  31. }
  32. inline void Work(int Id) {
  33. int pw = 1;
  34. for(register int i = 1; i <= K; ++i) {
  35. int L = Line[Id].l / pw % 10, R = Line[Id].r / pw % 10;
  36. int Prex = Line[Id].l / (pw * 10) * (pw * 10), Prey = Line[Id].r / (pw * 10) * (pw * 10);
  37. if(Prex == Prey) {
  38. for(register int j = L + 1; j < R; ++j)
  39. for(register int k = 1; k < 10; ++k)
  40. if(j != k && Vst[j][k]) {
  41. int Cntx = Prex + k * pw;
  42. int Cnty = Prey + (k + 1) * pw - 1;
  43. Calc(Cntx, Cnty, j, k);
  44. }
  45. }
  46. else {
  47. for(register int j = L + 1; j < 10; ++j)
  48. for(register int k = 1; k < 10; ++k)
  49. if(j != k && Vst[j][k]) {
  50. int Cntx = Prex + k * pw;
  51. int Cnty = Prex + (k + 1) * pw - 1;
  52. Calc(Cntx, Cnty, j, k);
  53. }
  54. for(register int j = 1; j < R; ++j)
  55. for(register int k = 1; k < 10; ++k)
  56. if(j != k && Vst[j][k]) {
  57. int Cntx = Prey + k * pw;
  58. int Cnty = Prey + (k + 1) * pw - 1;
  59. Calc(Cntx, Cnty, j, k);
  60. }
  61. }
  62. int ax = Line[Id].l % pw, ay = pw - 1, bx = 0, by = Line[Id].r % pw;
  63. if(Prex == Prey && L == R) ay = by, bx = ax;
  64. for(register int j = 1; j < 10; ++j) {
  65. if(j != L && Vst[L][j]) {
  66. int Cntx = Prex + j * pw + ax;
  67. int Cnty = Prex + j * pw + ay;
  68. Calc(Cntx, Cnty, L, j);
  69. }
  70. if(j != R && Vst[R][j]) {
  71. int Cntx = Prey + j * pw + bx;
  72. int Cnty = Prey + j * pw + by;
  73. Calc(Cntx, Cnty, R, j);
  74. }
  75. }
  76. pw *= 10;
  77. }
  78. }
  79. signed main() {
  80. // freopen("laser.in", "r", stdin);
  81. // freopen("laser.out", "w", stdout);
  82. n = read(), K = read();
  83. int tot = 0;
  84. memset(Vst, 1, sizeof(Vst));
  85. for(register int i = 1; i <= n; ++i) {
  86. int l = read(), r = read();
  87. if(i > 1 && Line[tot].r + 1 == l) Line[tot].r = r;
  88. else Line[++tot].l = l, Line[tot].r = r;
  89. }
  90. n = tot;
  91. for(register int i = 1; i <= n; ++i) Work(i);
  92. for(register int i = 1; i < 10; ++i)
  93. if(!Use[i]) {
  94. for(register int j = 1; j < 10; ++j)
  95. if(!Use[j] && Vst[i][j]) printf("%d", j), Use[j] = 1;
  96. printf("\n");
  97. }
  98. return 0;
  99. }

Summary

很好的分类讨论题,透露出很多思想。

比如对于每一位分开考虑限制,对于一个区间分端点与中间段考虑等等。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注