[关闭]
@dxbdly 2023-01-29T09:29:33.000000Z 字数 6865 阅读 226

2023.1.29 模拟赛记录

2023冬


期望分数:

T1 T2 T3 Sum
100 100 0 200

实际分数:

T1 T2 T3 Sum Rank
70 100 0 170 5

Summary

T1:代码有实现上的小问题,错将 变量的意义搞反,并且没有给 附初值。
T2:AC,但状态设计得稍显冗余
T3:看到无向图上随机游走问题,先入为主的认为一定是高斯消元解决,并且没有积累过这种拆平方期望的套路,导致没有想出来。

T1 GotoAndPlay

Description

我们用一张连通图来表示整个西湖的范围,每棵容小松鼠逗留的树都用
这张图上的一个点来表示。小松鼠能够通过只跳一次互相到达的两棵树用
图上的一条无向边来连接。 
 吃撑了的小松鼠有些神志不清,每次她连跳两条边之后才会在到达的那
个点上休息。她想知道,是否存在一种连续的跳法,使得她有机会在所有
的树上都休息至少一次。
 对于这种跳法,你可以任选起点,允许重复经过边,允许重复经过点。
 但是超萌小松鼠是一只有梦想的小松鼠,她有时能够突破自己的极限,
使一些原本无法互相到达的两个点能够通过一次跳跃互相到达。

Solution

稍微手玩一下+想一下,很容易发现若图中有奇环,则一定是 "YES",那么问题变为二分图判定。

染色,每次询问看两个端点是否同色冲突即可。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. inline int read() {
  5. int x = 0;
  6. char c = getchar();
  7. bool f = 0;
  8. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  9. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  10. return f ? -x : x;
  11. }
  12. const int maxn = 1e6 + 5, maxm = 2 * 1e6 + 5;
  13. int n, m, Q, flag;
  14. struct node {
  15. int v, nex;
  16. }edge[maxm << 1];
  17. int head[maxn], len;
  18. int Col[maxn];
  19. inline void make_map(int u, int v) {
  20. len++;
  21. edge[len].nex = head[u];
  22. edge[len].v = v;
  23. head[u] = len;
  24. }
  25. inline bool Search(int x, int col) {
  26. Col[x] = col;
  27. bool fl = 1;
  28. for(register int i = head[x]; i; i = edge[i].nex) {
  29. int y = edge[i].v;
  30. if(Col[y] != -1) {
  31. if(Col[y] ^ Col[x]) continue;
  32. else return 0;
  33. }
  34. fl &= Search(y, col ^ 1);
  35. }
  36. return fl;
  37. }
  38. int main() {
  39. // freopen("GotoAndPlay.in", "r", stdin);
  40. // freopen("GotoAndPlay.out", "w", stdout);
  41. n = read(), m = read();
  42. flag = 1;
  43. for(register int i = 1; i <= m; ++i) {
  44. int u = read(), v = read();
  45. make_map(u, v), make_map(v, u);
  46. }
  47. fill(Col + 1, Col + n + 1, -1);
  48. for(register int i = 1; i <= n; ++i)
  49. if(Col[i] == -1) flag &= Search(i, 0);
  50. Q = read();
  51. while(Q--) {
  52. int u = read(), v = read();
  53. if(!flag) { printf("Yes\n"); continue; }
  54. if(Col[u] == Col[v]) printf("Yes\n");
  55. else printf("No\n");
  56. }
  57. return 0;
  58. }

T2 StopAllSounds

Description

超萌游戏机之所以拥有这个名字,是因为它的屏幕是一个n  2的矩形。
小松鼠接过游戏机,开始了她的第一个游戏:俄罗斯方块。
 考虑到小松鼠的智商,游戏机里的方块只有下面四种,方块按顺序下落,
 可以在任意时刻(甚至是下落前)对其进行不限次数的旋转操作。
 由于四种方块最小宽度都为2,因此下落的时候在水平方向上是不能够移
动的。我们称当前方块下落的过程完成了,当且仅当其再往下移动一个单
位就会与之前覆盖的方块有部分相重叠。小松鼠想要知道,在这个n  2的
游戏界面中,一共会出现多少种游戏状态。游戏状态指单次方块下落的过
程完成后,不要求游戏结束(即不要求第1行非空),且界面中出现的必须
是完整的方块。
 两种游戏状态被认为是相同的,当且仅当游戏界面中的每一个格子两种
状态下被覆盖的方块类型都相同(或都不被覆盖)。
 再次考虑到小松鼠的智商,答案模 输出。

Solution

比较显然的状压 DP 题,一种比较好的状压方式是考虑当前“表面”的缺口。

分别记:表面没有缺口,表面左侧有一个缺口,表面右侧有一个缺口,表面左侧有两个缺口,表面右侧有两个缺口

分讨转移即可。

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 = 1e6 + 5, mod = 1e9 + 7;
  14. int n;
  15. int f[maxn][2][2][2][2], Answer;
  16. inline void Add(int &x, int y) { x = (x + y) % mod; }
  17. signed main() {
  18. freopen("StopAllSounds.in", "r", stdin);
  19. freopen("StopAllSounds.out", "w", stdout);
  20. n = read();
  21. f[0][1][1][1][1] = 1;
  22. for(register int i = 1; i <= n; ++i) {
  23. for(register int a = 0; a < 2; ++a) {
  24. Add(f[i][0][0][0][1], f[i - 1][0][1][a][1]);
  25. Add(f[i][0][0][1][0], f[i - 1][1][0][1][a]);
  26. }
  27. Add(f[i][0][0][1][1], f[i - 1][1][1][0][1]);
  28. Add(f[i][0][0][1][1], f[i - 1][1][1][1][0]);
  29. Add(f[i][0][0][1][1], f[i - 1][1][1][1][1]);
  30. if(i == 1) continue;
  31. for(register int a = 0; a < 2; ++a)
  32. for(register int b = 0; b < 2; ++b) {
  33. Add(f[i][1][1][1][1], f[i - 2][1][0][a][b]);
  34. Add(f[i][1][1][1][1], f[i - 2][0][1][a][b]);
  35. Add(f[i][1][1][1][1], f[i - 2][1][1][a][b]);
  36. }
  37. for(register int a = 0; a < 2; ++a) {
  38. Add(f[i][0][1][1][1], f[i - 2][1][0][1][a]);
  39. Add(f[i][0][1][1][1], f[i - 2][0][0][a][1]);
  40. }
  41. for(register int a = 0; a < 2; ++a) {
  42. Add(f[i][1][0][1][1], f[i - 2][0][1][a][1]);
  43. Add(f[i][1][0][1][1], f[i - 2][0][0][1][a]);
  44. }
  45. if(i >= 4) {
  46. Add(f[i][1][1][1][1], f[i - 3][0][0][1][0]);
  47. Add(f[i][1][1][1][1], f[i - 3][0][0][0][1]);
  48. Add(f[i][1][1][1][1], f[i - 3][0][0][1][1]);
  49. }
  50. Add(f[i][1][1][1][0], f[i - 2][0][1][1][1]);
  51. for(register int a = 0; a < 2; ++a)
  52. Add(f[i][1][1][1][0], f[i - 2][0][0][1][a]);
  53. for(register int a = 0; a < 2; ++a) {
  54. Add(f[i][0][1][1][1], f[i - 2][0][0][1][a]);
  55. Add(f[i][0][1][1][1], f[i - 2][0][1][a][1]);
  56. }
  57. Add(f[i][0][1][0][1], f[i - 2][0][0][1][0]);
  58. Add(f[i][0][1][0][1], f[i - 2][0][0][0][1]);
  59. Add(f[i][0][1][0][1], f[i - 2][0][0][1][1]);
  60. }
  61. for(register int i = 1; i <= n; ++i) {
  62. for(register int a = 0; a < 2; ++a) {
  63. Add(Answer, f[i][1][0][1][a]);
  64. Add(Answer, f[i][0][1][a][1]);
  65. }
  66. Add(Answer, f[i][1][1][0][1]), Add(Answer, f[i][1][1][1][0]), Add(Answer, f[i][1][1][1][1]);
  67. }
  68. printf("%lld\n", Answer + 1);
  69. return 0;
  70. }

T3 UpdateAfterEvent

Description

给定一张 个点的无向图,每个点上有 只松鼠,每个时刻,每只松鼠会等概率移动到相邻的点,概率为 ,若最终状态下,某个点上松鼠的数量为 ,则这个点的价值为 ,问经过 的时间后,所有点的价值之和的期望。

数据分为两个Sub

Sub1 :
Sub2 :

Solution

考虑这个价值是个平方的柿子,而平方的式子是不能用线性性拆开的,我们考虑改变计算价值的形式。

考虑到

我们以每只松鼠为视角,给每只松鼠一个标号,假定松鼠的行动是按标号先后顺序行动的。

那么我们可以将所求改变为 每只松鼠到达终点后造成的贡献的和的期望。

每只松鼠到达终点后造成的贡献就是这个点现在有的松鼠数量。

这个期望是可以用线性性拆开的。

我们考虑 Sub1,考虑求出 表示第 个点的松鼠终点走到 的概率。

这个东西显然可以拿度数的倒数矩阵,做 次方的矩阵快速幂得到。

那么计算答案,考虑枚举两个松鼠的起点 ,终点 ,那么答案为

特殊的,当 时,需要将 改成

复杂度

考虑Sub2

由于 ,不再需要矩阵快速幂。

而上面那个计算答案的方式,显然可以将 分开,用前缀和优化掉一维,这个优化同样适用于Sub1,但不解决瓶颈

复杂度

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 = 1005, maxm = 105, mod = 1e9 + 7;
  14. int n, T;
  15. int Val[maxn], mapp[maxn][maxn], Du[maxn], Inv[maxn], Answer;
  16. struct Matrix {
  17. int num[maxm][maxm];
  18. inline void Clear() { memset(num, 0, sizeof(num)); }
  19. inline int &operator () (int x, int y) { return num[x][y]; }
  20. inline void Init() {
  21. Clear();
  22. for(register int i = 1; i <= n; ++i) num[i][i] = 1;
  23. }
  24. }Base, f;
  25. inline Matrix operator *(Matrix A, Matrix B) {
  26. Matrix C; C.Clear();
  27. for(register int i = 1; i <= n; ++i)
  28. for(register int j = 1; j <= n; ++j)
  29. for(register int k = 1; k <= n; ++k)
  30. C(i,j) = (C(i,j) + A(i, k) * B(k, j) % mod) % mod;
  31. return C;
  32. }
  33. inline Matrix Matrix_Ksm(Matrix A, int B) {
  34. Matrix res; res.Init();
  35. while(B) {
  36. if(B & 1) res = res * A;
  37. A = A * A, B >>= 1;
  38. }
  39. return res;
  40. }
  41. inline int Ksm(int A, int B) {
  42. int res = 1;
  43. while(B) {
  44. if(B & 1) res = res * A % mod;
  45. A = A * A % mod, B >>= 1;
  46. }
  47. return res;
  48. }
  49. inline void Ready() {
  50. for(register int i = 1; i <= n; ++i) Inv[i] = Ksm(i, mod - 2);
  51. }
  52. signed main() {
  53. freopen("UpdateAfterEvent.in", "r", stdin);
  54. freopen("UpdateAfterEvent.out", "w", stdout);
  55. n = read(), T = read();
  56. Ready();
  57. for(register int i = 1; i <= n; ++i) Val[i] = read();
  58. for(register int i = 1; i <= n; ++i)
  59. for(register int j = 1; j <= n; ++j) {
  60. mapp[i][j] = read();
  61. if(mapp[i][j]) Du[i]++;
  62. }
  63. if(T == 1) {
  64. for(register int i = 1; i <= n; ++i) {
  65. int Sum = 0;
  66. for(register int j = 1; j <= n; ++j) {
  67. if(!mapp[i][j]) continue;
  68. int Cnt = Val[j] * Inv[Du[j]] % mod;
  69. Answer = (Answer + Sum * Cnt % mod) % mod;
  70. Sum = (Sum + Cnt) % mod;
  71. }
  72. }
  73. for(register int i = 1; i <= n; ++i) {
  74. int Cnt = Val[i] * (Val[i] - 1) / 2 % mod;
  75. Answer = (Answer + Inv[Du[i]] * Cnt % mod) % mod;
  76. }
  77. printf("%lld\n", Answer);
  78. return 0;
  79. }
  80. else {
  81. Base.Clear();
  82. for(register int i = 1; i <= n; ++i)
  83. for(register int j = 1; j <= n; ++j)
  84. if(mapp[i][j]) Base(i, j) = Inv[Du[i]];
  85. Base = Matrix_Ksm(Base, T);
  86. for(register int i = 1; i <= n; ++i)
  87. for(register int j = i + 1; j <= n; ++j)
  88. for(register int k = 1; k <= n; ++k) {
  89. int A = Base(i, k) * Val[i] % mod, B = Base(j, k) * Val[j] % mod;
  90. Answer = (Answer + A * B % mod) % mod;
  91. }
  92. for(register int i = 1; i <= n; ++i)
  93. for(register int j = 1; j <= n; ++j) {
  94. int A = Base(i, j) * Base(i, j) % mod;
  95. int B = Val[i] * (Val[i] - 1) / 2 % mod;
  96. Answer = (Answer + A * B % mod) % mod;
  97. }
  98. printf("%lld\n", Answer);
  99. }
  100. return 0;
  101. }

Summary

非常巧妙的期望转换方式,面对平方的期望贡献,不只能用完全平方拆开,还可以考虑分步重写贡献。

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