[关闭]
@dxbdly 2022-11-13T06:57:46.000000Z 字数 4287 阅读 212

CSP-S2022题解

2022秋


[CSP2022 T3] 星战

Description

Solution

神奇的随机化题。

考虑询问的两个条件:

简单来说就是 所有点出度为 都可以走到环上

其实这样的图有个名称,叫 内向基环树森林,而一张图是内向基环树森林的充要条件就是 所有点出度为1

所以第二个条件实际上是无用的,我们只需要考虑第一个条件,这点在考场上稍微手玩一下也能看出来。

然后大概有两类想法:

Sum Hash

考虑这样一个等价一一对应的想法:

所有点出度为 所有点出度和为

但这显然是错的,他显然是个充分但不必要的逻辑。

例如,出度为 的图 与出度为 的图,这样的情况显然会出问题。

换句话说,其实是这个出度和 ,他可以被组合出来的形式太多,而我们要求的所有点的出度形式太特殊了。

同时,我们做的是一个类似一一对应的操作,而问题也正出在对应存在冲突上,这都很满足 hash 的特点。

所以有一个想法:

我们随机给点赋权值,令 表示所有连向 的点的点权和, 表示整个图所有点的点权和

那么上述的一一对应关系可以转化为:

满足这个条件,我们就认为它满足题意。

四个操作显然都非常好维护。

一种奇怪的做法

咕咕咕。

Code

  1. //The Code Is From Dawn

[CSP2022 T4] 数据传输

Description

此题解参考GraphCity的blog

请大家点个赞bushi

Solution

暂时省略一些部分分做法。

我们直接来讨论一下 的情况。

首先对于

显然答案就是两点之间路径点权和,树剖或者倍增维护即可。

对于

我们开始仔细分析此题的一些性质。

首先要提出一个很重要的思考点,最优的方案是否会走出两点之间的路径

这是影响这题重要的问题,可以稍微手玩一下。

至少我们能大概猜出,即使走出了两点的路径,也不会走得特别偏。

所以不妨先大胆猜测,最优方案一定在两点间的路径上

对于 ,可以证明这是对的。

那么可以考虑 DP,设 表示跳到点 的最小答案。

显然有

我们先不考虑如何优化。

对于

我们发现之前的结论被推翻了。

时,你可以从一个点 的儿子,跳到与他相邻的其他儿子,而这有可能成为更优的情况。

我们考虑修正一下这个结论,发现即使要跳出这条链,最多也只会跳到 点的唯一一个儿子,而这个儿子显然应该是点权最小的。

我们记: 的儿子最小点权为

再修改一下状态, 表示跳到点 ,是否跳出这条链的答案。

至此可以拿到 的好成绩。

考虑优化。

我们从 的方程入手。

带取 与 加和的线性方程,有没有想到什么?

NOIP2018 保卫王国

他显然可以用 DDP,而且他不带修!

Graphcity 的 DDP 浅谈

我们把 的方程重新构造一下。

我们发现他只与要跳到的点与 的距离有关。

设: 表示 跳到距离点 的答案。

然后把三种情况都用矩阵表示:略。

然后直接把这个转移矩阵倍增出来,就解决了此题。

  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, INF = 1e18 + 7;
  14. int n, Q, K;
  15. int Val[maxn], num[maxn], f[maxn][25];
  16. struct node {
  17. int v, nex;
  18. }edge[maxn << 1];
  19. int head[maxn], len, Dep[maxn];
  20. inline void make_map(int u, int v) {
  21. len++;
  22. edge[len].nex = head[u];
  23. edge[len].v = v;
  24. head[u] = len;
  25. }
  26. struct Matrix {
  27. int num[3][3];
  28. inline void Clear() {
  29. for(register int i = 0; i < 3; ++i)
  30. for(register int j = 0; j < 3; ++j) num[i][j] = INF;
  31. }
  32. inline void Out() {
  33. for(register int i = 0; i < 3; ++i) {
  34. for(register int j = 0; j < 3; ++j) printf("%lld ", num[i][j]);
  35. printf("\n");
  36. }
  37. }
  38. inline int& operator() (int i, int j) { return num[i][j]; }
  39. }Base[maxn], Up[maxn][25], Down[maxn][25];
  40. inline Matrix Init(int op = 0, int id = 0) {
  41. Matrix A; A.Clear();
  42. if(!op) {
  43. for(register int i = 0; i < 3; ++i) A(i, i) = 0;
  44. }
  45. else if(op == 1){
  46. if(K == 1) A(0, 0) = Val[id], A(1, 1) = A(2, 2) = 0;
  47. if(K == 2) A(0, 0) = A(0, 1) = Val[id], A(1, 0) = A(2, 2) = 0;
  48. if(K == 3) {
  49. A(0, 0) = A(0, 1) = A(0, 2) = Val[id];
  50. A(1, 0) = 0, A(1, 1) = num[id], A(1, 2) = num[id] + Val[id];
  51. A(2, 1) = 0;
  52. }
  53. }
  54. else {
  55. for(register int i = 0; i < 3; ++i) A(i, i) = 0;
  56. A(0, 0) = Val[id];
  57. if(K == 3) A(0, 1) = Val[id] + num[id];
  58. }
  59. return A;
  60. }
  61. inline Matrix operator * (Matrix A, Matrix B) {
  62. Matrix C; C.Clear();
  63. for(register int i = 0; i < 3; ++i)
  64. for(register int j = 0; j < 3; ++j)
  65. for(register int k = 0; k < 3; ++k) C(i, j) = min(C(i, j), A(i, k) + B(k, j));
  66. return C;
  67. }
  68. inline void Search(int x, int fa) {
  69. f[x][0] = fa, Up[x][0] = Down[x][0] = Base[x], Dep[x] = Dep[fa] + 1;
  70. for(register int i = 1; i <= 20; ++i) {
  71. f[x][i] = f[f[x][i - 1]][i - 1];
  72. Up[x][i] = Up[f[x][i - 1]][i - 1] * Up[x][i - 1];
  73. Down[x][i] = Down[x][i - 1] * Down[f[x][i - 1]][i - 1];
  74. }
  75. for(register int i = head[x]; i; i = edge[i].nex) {
  76. int y = edge[i].v;
  77. if(y == fa) continue;
  78. Search(y, x);
  79. }
  80. }
  81. inline Matrix LCA(int x, int y) {
  82. Matrix A = Init(), B = Init();
  83. if(Dep[x] < Dep[y]) B = Base[y], y = f[y][0];
  84. for(register int i = 20; i >= 0; --i) {
  85. if(Dep[f[x][i]] >= Dep[y]) A = Up[x][i] * A, x = f[x][i];
  86. if(x == y) return B * Base[x] * A;
  87. }
  88. for(register int i = 20; i >= 0; --i)
  89. if(f[x][i] != f[y][i]) A = Up[x][i] * A, B = B * Down[y][i], x = f[x][i], y = f[y][i];
  90. B = B * Base[y] * Base[f[y][0]], A = Base[x] * A;
  91. return B * A;
  92. }
  93. inline bool cmp(int x, int y) { return Val[x] < Val[y]; }
  94. inline int Solve(int x, int y) {
  95. if(x == y) return Val[x];
  96. if(Dep[x] < Dep[y]) swap(x, y);
  97. Matrix Answer = LCA(f[x][0], y) * Init(2, x);
  98. return Answer(0, 0);
  99. }
  100. signed main() {
  101. #ifndef ONLINE_JUDGE
  102. freopen("D.in", "r", stdin);
  103. freopen("D.out", "w", stdout);
  104. #endif
  105. n = read(), Q = read(), K = read();
  106. for(register int i = 1; i <= n; ++i) Val[i] = read(), num[i] = INF;
  107. for(register int i = 1; i < n; ++i) {
  108. int u = read(), v = read();
  109. make_map(u, v), make_map(v, u), num[u] = min(num[u], Val[v]), num[v] = min(num[v], Val[u]);
  110. }
  111. for(register int i = 1; i <= n; ++i) Base[i] = Init(1, i);
  112. Search(1, 0);
  113. while(Q--) {
  114. int u = read(), v = read();
  115. printf("%lld\n", Solve(u, v));
  116. }
  117. return 0;
  118. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注