[关闭]
@2368860385 2020-11-07T03:00:51.000000Z 字数 4722 阅读 176

前:day3考试

衢州


考试总结

T1

没见过这种hash。。。
于是暴力,后面链的情况写了,过了自己造的样例,然后先交了一下。之后写对拍,然后发现链的情况有问题,然后改,然后就忘记在交一遍了。。

T2

5分感觉好少,于是想30分的做法。。。
于是想到是先是一棵树,只有几十个双联通分量。对于非双联通分量里的点都是-1,叶子节点特判一下,强联通分量里的点需要处理一下。

处理好麻烦!!!!!

为什么这样想好像对,写不对?细节太多,复杂度还不大正确,出题人不是这样想的。

T3

5分,直接枚举,还有地方没处理好。


题解

T1

对一棵子树求出 hash ,放到set里,然后在另一棵树里查询。
可以开n个set,表示大小为n的子树。

当前子树的大小为k,那么对于另一棵子树中,所有对应的点,的lca,以这个lca为根的子树中,大小为看k。
可以放到dfs序上处理。

集合hash,只查询元素有没有,不需要查询顺序。

zobrist hashing

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<vector>
  8. #include<set>
  9. using namespace std;
  10. typedef long long LL;
  11. inline int read() {
  12. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  13. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  14. }
  15. const int N = 200010;
  16. vector<int>T[N];
  17. LL key[N], Hash[N];
  18. int siz[N], Ans;
  19. set<LL>s[N];
  20. void init(int x) {
  21. LL a = (rand() << 15) | rand();
  22. LL b = (rand() << 15) | rand();
  23. key[x] = a * b;
  24. }
  25. void dfs1(int u,int pa) {
  26. siz[u] = 1, Hash[u] = key[u];
  27. int sz = T[u].size();
  28. for (int i=0; i<sz; ++i) {
  29. int v = T[u][i];
  30. if (v == pa) continue;
  31. dfs1(v,u);
  32. siz[u] += siz[v];
  33. Hash[u] ^= Hash[v];
  34. }
  35. if (u == 1) return ;
  36. s[siz[u]].insert(Hash[u]);
  37. }
  38. void dfs2(int u,int pa) {
  39. siz[u] = 1, Hash[u] = key[u];
  40. int sz = T[u].size();
  41. for (int i=0; i<sz; ++i) {
  42. int v = T[u][i];
  43. if (v == pa) continue;
  44. dfs2(v,u);
  45. siz[u] += siz[v];
  46. Hash[u] ^= Hash[v];
  47. }
  48. if (u == 1) return ;
  49. if (s[siz[u]].count(Hash[u])) Ans ++;
  50. }
  51. int main() {
  52. int n = read();
  53. for (int i=1; i<=n; ++i) init(i);
  54. for (int i=1; i<n; ++i) {
  55. int u = read(),v = read();
  56. T[u].push_back(v);
  57. T[v].push_back(u);
  58. }
  59. dfs1(1,0);
  60. for (int i=1; i<=n; ++i) T[i].clear();
  61. for (int i=1; i<n; ++i) {
  62. int u = read(),v = read();
  63. T[u].push_back(v);
  64. T[v].push_back(u);
  65. }
  66. dfs2(1,0);
  67. cout << Ans;
  68. return 0;
  69. }

T2

题意:对于每个点i,求出从这个点出发,选一条边,然后遍历完所有点(中途不能回到i,每条边可以走多次),再从选的边回到这个点的经过的最大的边最小是多少。

m=n+60,求出MST,枚举每个点被删除后,然后再枚举剩余60条边,加入。

先求一遍MST,然后从小到大,枚举非树边。

考虑返祖边:u->v,边权为w,这条边,那么所有fa[u]->son[v]的点(就是这条路径经过的点中,不包含uv的点),会对更新这些点(称为更新点,假设i是这些点中的一个,因为去掉这个点i后,形成了degree[i]个联通块,需要把这些联通块在连起来,而这条边就会使 i的fa所在的联通块 和 u所在的联通块合起来)。

如何更新贡献:对于一个 路径点(路径u->v上的点)i,其父节点p(要求p一定是更新点,所以不能是v),由于去掉p后,它的子节点i,可以连向p的fa所在的联通块,所以更新ans[p]=w。因为i对p产生了贡献,所以称i为贡献点。以此类推,就可以更新完所有的更新点。
i做为贡献点,更新了p,之后i不会再成为贡献点去更新p了。因为去掉p后形成的联通块中,i所在的联通块,用u->v这条边连接是最优的,所以就用u->v连接了。所以如果再有路径可以让i对p产生贡献,可以直接跳到p。而p也许已经做过贡献点了,又跳到了p的fa……所以用并查集维护,将做过贡献点的指向最近的一个没做过贡献点。

合并:u就是贡献点,v是更新点(u相当于i,v相当于p)。

  1. void Merge(int u,int v) {
  2. u = find(u), v = find(v);
  3. if (u != v) fa[u] = v; // 顺序不能反
  4. }

横插边:u->v,可以拆成u->lca,v->lca两条返租边来做,lca也是更新点,而拆成的两路径不能更新lca,所以最后判断一下。

核心代码,路径u->v,权值w,u是贡献点,p是更新点。

  1. while (deth[p] > deth[v]) {
  2. ans[p] = w; dgr2[p]++; Merge(u, p);
  3. u = find(u), p = f[u][0];
  4. }

统计答案:如果一个点更新了degree[i]-1次,那么说明将i去掉后,可以形成联通块,答案是ans[i],否则答案是-1。特判一下叶子节点。

每个点最多做一次贡献点,如果lca使用求的话,复杂度是

tips:撤销操作,用一个栈记录操作序列。
tipS树套树:log,维护外面树对套树的映射。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<cctype>
  6. #include<vector>
  7. using namespace std;
  8. typedef long long LL;
  9. inline int read() {
  10. int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch=getchar()) if (ch=='-') f = -1;
  11. for (; isdigit(ch); ch=getchar()) x = x * 10 + ch - '0'; return x * f;
  12. }
  13. const int N = 1000100;
  14. struct Edge{
  15. int u, v, w;
  16. Edge() {}
  17. Edge(int a,int b,int c) {u = a, v = b, w = c;}
  18. bool operator < (const Edge &A) const {
  19. return w < A.w;
  20. }
  21. }e[N];
  22. int Enum, tot;
  23. int g[N], fa[N], f[N][21], dgr1[N], dgr2[N], deth[N], ans[N];
  24. vector<int>T[N];
  25. int find(int x) {
  26. return x == fa[x] ? x : fa[x] = find(fa[x]);
  27. }
  28. int Kruskal(int n,int m) {
  29. for (int i=1; i<=n; ++i) fa[i] = i;
  30. int cnt = 0, ans = -1;
  31. sort(e + 1, e + m + 1);
  32. for (int i=1; i<=m; ++i) {
  33. int u = e[i].u, v = e[i].v;
  34. int fu = find(u), fv = find(v);
  35. if (fu == fv) {
  36. g[++tot] = i; continue;
  37. }
  38. ++cnt; fa[fu] = fv; ans = e[i].w;
  39. dgr1[u] ++; dgr1[v] ++;
  40. T[u].push_back(v); T[v].push_back(u);
  41. }
  42. if (cnt == n - 1) return ans;
  43. return -1;
  44. }
  45. void dfs(int u,int fa) {
  46. deth[u] = deth[fa] + 1;
  47. for (int sz=T[u].size(), i=0; i<sz; ++i) {
  48. int v = T[u][i];
  49. if (v == fa) continue;
  50. f[v][0] = u;
  51. dfs(v, u);
  52. }
  53. }
  54. int Lca(int u,int v) {
  55. if (deth[u] < deth[v]) swap(u, v);
  56. int d = deth[u] - deth[v];
  57. for (int i=20; i>=0; --i)
  58. if (d & (1 << i)) u = f[u][i];
  59. if (u == v) return u;
  60. for (int i=20; i>=0; --i)
  61. if (f[u][i] != f[v][i])
  62. u = f[u][i], v = f[v][i];
  63. return f[u][0];
  64. }
  65. void Merge(int u,int v) {
  66. u = find(u), v = find(v);
  67. if (u != v) fa[u] = v;
  68. }
  69. int main() {
  70. int n = read(), m = read();
  71. for (int i=1; i<=m; ++i) {
  72. int u = read(), v = read(), w = read();
  73. e[++Enum] = Edge(u, v, w);
  74. }
  75. int Minans = Kruskal(n, m);
  76. if (Minans == -1) {
  77. cout << -n; return 0;
  78. }
  79. dfs(1, 0);
  80. for (int j=1; j<=20; ++j)
  81. for (int i=1; i<=n; ++i)
  82. f[i][j] = f[f[i][j-1]][j-1];
  83. for (int i=1; i<=n; ++i) fa[i] = i;
  84. for (int i=1; i<=tot; ++i) {
  85. int u = e[g[i]].u, v = e[g[i]].v, w = e[g[i]].w, p, tu, tv;
  86. if (deth[u] < deth[v]) swap(u, v);
  87. int x = Lca(u, v);
  88. if (x == v) {
  89. u = find(u), p = f[u][0];
  90. while (deth[p] > deth[v]) {
  91. ans[p] = w; dgr2[p]++; Merge(u, p);
  92. u = find(u), p = f[u][0];
  93. }
  94. }
  95. else {
  96. tu = u, tv = v;
  97. u = find(u), p = f[u][0];
  98. while (deth[p] > deth[x]) {
  99. ans[p] = w; dgr2[p]++; Merge(u, p);
  100. u = find(u), p = f[u][0];
  101. }
  102. v = find(v), p = f[v][0];
  103. while (deth[p] > deth[x]) {
  104. ans[p] = w; dgr2[p]++; Merge(v, p);
  105. v = find(v), p = f[v][0];
  106. }
  107. u = find(tu), v = find(tv);
  108. if (u != v) {
  109. if (deth[u] < deth[v]) swap(u, v);
  110. ans[x] = w; dgr2[x]++; Merge(u, v);
  111. }
  112. }
  113. }
  114. LL Ans = 0;
  115. for (int i=1; i<=n; ++i) {
  116. if (dgr1[i] == 1) Ans += Minans;
  117. else if (dgr2[i] < dgr1[i] - 1) Ans --;
  118. else Ans += max(Minans, ans[i]);
  119. }
  120. cout << Ans;
  121. return 0;
  122. }

T3


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