[关闭]
@dxbdly 2023-08-22T09:37:40.000000Z 字数 5199 阅读 172

2023.8.17 nfls集训Day10 考试总结

2023暑nfls


前言:为什么那么多人做过 最大XOR和路径

Result

F G H Sum
100 100 0 200

Progress

眼镜坏了,考试时间 -1h

一回来,过 的人比过 多?

开题:

F:一开始以为能力值不能重复,感觉是个不好做的数数
G:啊?这不就是 最大XOR和路径,也的确是线性基典题(
H:感觉很熟悉啊,又是 DS

回忆了一下 ,感觉这是个初见不是很可做的题啊(

很好写,很快过了。

没什么思路,不知道啥时候,发现看错题了。

可重的话是不是随便做啊!

考虑对于一个数 ,能接在他后面的是

这是很明显的整除分块形式。

如果 很小, 很大,我们可以分块建图,每个块向能到的块连边。

那么把邻接矩阵拿下来做矩阵快速幂就好了。

至于这题, 很大, 很小,就更加好做,还是按整除分块将 变成 级别,那就暴力 DP 就好了

的时候没啥时间了,想了一个跟正解长得很像的东西,但是有出入。

F 能力值拟定方案

考虑暴力

根据整除分块理论, 只有 种取值。

也就是说我们可以将点分成 类,将每一类当做点 DP,即可做到

G 最大 XOR

如果不存在路径的要求,我们可以用线性基取出 XOR 的最大值。

也就是说我们需要想办法描述一条路径。

首先我们考虑如何描述路径可重,会发现,最终异或的东西,是一条 的不可重路径加上一堆环。

我们先不考虑环,考虑怎么描述一条不可重路径。

这里有个很厉害的东西,针对异或的特性,我们其实有:

每条不可重路径都可以写成 任意一条不可重路径 加上一堆环。

换句话说,我们可以通过加环,将所有不同的不可重路径规约到任意一条不可重路径上。

那我们就只需要处理环。

一个很明显的思路,我们只需要将所有环加到线性基里,就相当于完成了环的贡献。

但是所有环的数量显然太多。

这里是第二个很厉害的东西,考虑一个复杂的大环可以拆成很多个小环,我们只需要将这些基本环加进去就好了。

那么怎么找出基本环呢。

我们取一颗 dfs 生成树,将边区分为树边和非树边,那么每条非树边会和树边形成一个环,我们可以证明,每一条非树边与每一个单位环一一对应。

那我们只要建出一颗dfs,每次遇到非树边就将环加入线性基,最后随意取出一条 的路径放到线性基里求最大值就好了。

  1. #include<bits/stdc++.h>
  2. #define int long long
  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 = 1e5 + 5;
  13. int n, m;
  14. struct Xor_Base {
  15. int A[65];
  16. inline void Init() { memset(A, 0, sizeof(A)); }
  17. inline void Insert(int x) {
  18. for(register int i = 62; i >= 0; --i)
  19. if(x >> i & 1) {
  20. if(!A[i]) { A[i] = x; return ; }
  21. x ^= A[i];
  22. }
  23. }
  24. inline int Query(int x) {
  25. int Answer = x;
  26. for(register int i = 62; i >= 0; --i) Answer = max(Answer, Answer ^ A[i]);
  27. return Answer;
  28. }
  29. }Base;
  30. struct node {
  31. int v, nex;
  32. int w;
  33. }edge[maxn << 1];
  34. int head[maxn], len, Vst[maxn], Val, Dis[maxn];
  35. inline void make_map(int u, int v, int w) {
  36. len++;
  37. edge[len].nex = head[u];
  38. edge[len].v = v;
  39. edge[len].w = w;
  40. head[u] = len;
  41. }
  42. inline void Search(int x, int fa) {
  43. Vst[x] = 1, Dis[x] = Val;
  44. for(register int i = head[x]; i; i = edge[i].nex) {
  45. int y = edge[i].v, z = edge[i].w;
  46. if(Vst[y]) Base.Insert(Val ^ Dis[y] ^ z);
  47. else Val ^= z, Search(y, x), Val ^= z;
  48. }
  49. }
  50. signed main() {
  51. // freopen("G.in", "r", stdin);
  52. // freopen("G.out", "w", stdout);
  53. n = read(), m = read(); Base.Init();
  54. for(register int i = 1; i <= m; ++i) {
  55. int u = read(), v = read(), w = read();
  56. make_map(u, v, w), make_map(v, u, w);
  57. }
  58. Search(1, 0);
  59. printf("%lld\n", Base.Query(Dis[n]));
  60. return 0;
  61. }

H 大鱼吃小鱼

我们考虑一个暴力。

把所有鱼丢到权值线段树上,每次查询,我们线段树上二分出一个能吃的最大的东西,然后吃掉。

重复这个过程即可。

我们考察一下这个过程,考虑这个最大值的选取,我们假设第一次选到了第 大的鱼,那么如果吃下 后,能够吃下 ,则 会往后移,否则会一直往左移,知道能吃下

显然会一直重复这个过程。

也就是说我们可以压缩这个选取的过程,每次找到第一个比他大的,再找到一段最短的后缀,使其能够吃下它。

而由于每轮能吃掉一个比它大的,那么每次重量至少会变大一倍。

所以复杂度会达到

启示:

做这种合并的东西,可以考虑保证让它每次都变大一倍,就可以保证复杂度。

  1. #include<bits/stdc++.h>
  2. #define int long long
  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, INF = 1e18;
  13. int n, Q, len;
  14. int Val[maxn], ls[maxn << 1];
  15. struct Quer {
  16. int op, x, y;
  17. }Que[maxn];
  18. struct node {
  19. int l, r;
  20. int num, Sum;
  21. }Tree[maxn << 3], Cnt[maxn << 3];
  22. int St[maxn << 3], Vst[maxn << 3], top;
  23. #define lc(i) i << 1
  24. #define rc(i) i << 1 | 1
  25. inline void Push_Up(int i) {
  26. Tree[i].num = (Tree[lc(i)].num + Tree[rc(i)].num);
  27. Tree[i].Sum = (Tree[lc(i)].Sum + Tree[rc(i)].Sum);
  28. }
  29. inline void Build(int i, int l, int r) {
  30. Tree[i].l = l, Tree[i].r = r, Tree[i].num = Tree[i].Sum = 0;
  31. if(l == r) { return ; }
  32. int mid = (l + r) >> 1;
  33. Build(lc(i), l, mid), Build(rc(i), mid + 1, r);
  34. }
  35. inline void Update(int i, int l, int r, int x, int num) {
  36. if(l == r) {
  37. Tree[i].num += num, Tree[i].Sum += ls[l] * num;
  38. return ;
  39. }
  40. int mid = (l + r) >> 1;
  41. if(x <= mid) Update(lc(i), l, mid, x, num);
  42. else Update(rc(i), mid + 1, r, x, num);
  43. Push_Up(i);
  44. }
  45. inline int Find(int i, int l, int r, int x) {
  46. if(l == r) return Tree[i].num > 0 ? l : 0;
  47. int mid = (l + r) >> 1, pos = 0;
  48. if(x <= mid && Tree[lc(i)].num) pos = Find(lc(i), l, mid, x);
  49. if(pos) return pos;
  50. if(Tree[rc(i)].num) pos = Find(rc(i), mid + 1, r, x);
  51. return pos;
  52. }
  53. inline void Delte(int i) { St[++top] = i, Cnt[top] = Tree[i], Vst[i] = 1; }
  54. inline void ReBuild() { while(top) Tree[St[top]] = Cnt[top], Vst[St[top--]] = 0; }
  55. inline int Eat(int i, int l, int r, int x, int &num) {
  56. if(!Vst[i]) Delte(i);
  57. if(r <= x && Tree[i].Sum <= num) {
  58. num -= Tree[i].Sum; int Answer = Tree[i].num;
  59. Tree[i].Sum = Tree[i].num = 0;
  60. return Answer;
  61. }
  62. if(l == r) {
  63. int tot = (num % ls[l] ? num / ls[l] + 1 : num / ls[l]);
  64. num -= tot * ls[l], Tree[i].num -= tot, Tree[i].Sum -= ls[l] * tot;
  65. return tot;
  66. }
  67. int mid = (l + r) >> 1, tot = 0;
  68. if(x > mid) tot += Eat(rc(i), mid + 1, r, x, num);
  69. if(num > 0) tot += Eat(lc(i), l, mid, x, num);
  70. Push_Up(i);
  71. return tot;
  72. }
  73. inline int Solve(int now, int Limit) {
  74. if(now >= Limit) return 0;
  75. if(now <= ls[1]) return -1;
  76. int flag = 1, Nex, tot, Delta, Answer = 0;
  77. while(now < Limit) {
  78. Nex = Find(1, 1, len, lower_bound(ls + 1, ls + len + 1, now) - ls);
  79. if(!Nex) Nex = len + 1, tot = Limit;
  80. else tot = min(ls[Nex] + 1, Limit);
  81. Delta = tot - now;
  82. Answer += Eat(1, 1, len, Nex - 1, Delta);
  83. if(Delta > 0) { flag = 0; break; }
  84. now = tot - Delta;
  85. }
  86. ReBuild();
  87. return flag ? Answer : -1;
  88. }
  89. signed main() {
  90. // freopen("H.in", "r", stdin);
  91. // freopen("H.out", "w", stdout);
  92. n = read(), len = n;
  93. for(register int i = 1; i <= n; ++i) ls[i] = Val[i] = read();
  94. Q = read();
  95. for(register int i = 1; i <= Q; ++i) {
  96. int op = read(), x = read(), y;
  97. Que[i].op = op, Que[i].x = x;
  98. if(op == 1) y = read(), Que[i].y = y;
  99. else ls[++len] = x;
  100. }
  101. sort(ls + 1, ls + len + 1), len = unique(ls + 1, ls + len + 1) - ls - 1; ls[++len] = INF;
  102. Build(1, 1, len);
  103. for(register int i = 1; i <= n; ++i) Update(1, 1, len, lower_bound(ls + 1, ls + len + 1, Val[i]) - ls, 1);
  104. for(register int i = 1; i <= Q; ++i) {
  105. int op = Que[i].op, x = Que[i].x, y;
  106. if(op == 1) {
  107. y = Que[i].y;
  108. printf("%lld\n", Solve(x, y));
  109. }
  110. else if(op == 2) Update(1, 1, len, lower_bound(ls + 1, ls + len + 1, x) - ls, 1);
  111. else if(op == 3) Update(1, 1, len, lower_bound(ls + 1, ls + len + 1, x) - ls, -1);
  112. }
  113. return 0;
  114. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注