[关闭]
@dxbdly 2023-08-15T14:21:11.000000Z 字数 4406 阅读 228

2023.8.14 nfls集训Day6 考试总结

2023暑nfls


前言:被打爆了!!!破防实录

Result

F G H Sum
0 100 0 100

Progress

开题。

F:回文串串题,难受
G:网格图,相邻位置,40*40 范围 等于 网络流
H:我超,3000分的 DS,这是要维护个啥啊(事实证明到最后都没看懂题)

我超,怎么开完题有人 就过了啊!!!

从开 变成开 ,想了一会,考虑把先半径单增这个条件抽象成给每个点一个相对大小的权值,那可以规约到回文串中,做一个二关键字的回文。

那是不是 manacher 就好了,但是,我 不 会 写 manacher 了!!!

要吐了!!!

尝试回忆,回忆失败!我回去一定好好学串串!!!

无奈开 ,先黑白染色,玩一玩样例,猜了个结论叫最终状态一定是全为当前最大值。

感觉好对啊!直接开冲。

然后挂了,调了一会,感觉没问题,开始质疑!hack 成功!

为什么每天都在干这种事啊!!!!

冷静下来,重新观察一下性质,虽然结论挂了,但是结束状态长的样子很确定

找找操作里不变的东西,显然黑白差不变,然后推一推,改一改做法,又会了。

调了好久啊!!!但是最后还是过了。

回头看 ,收到信息可以 DP ???

为啥我 DP 也搞不出来啊!!!为啥大家都不带 log 啊,破防了!

LCIS 又是什么东西啊!太神奇了!最后只切 破防收场。

F 完美队形

郑重记录一下这个神奇的东西:LCIS

首先一增一减的东西是很不好维护的,我们把串复制一遍并倒过来

可以发现 递增回文序列 等于 当前两个序列上的 最长公共上升子序列(LCIS)

而这个 LCIS 的求法也很简单。

我们先考虑一个 的求法。

为 A 串前 ,B 串前 的 LCIS

如果

否则

可以发现这个 的合法只跟 有关,那显然可以记一下前缀 max 直接优化掉。

那这题就是个简单的 LCIS 板子。

G 做游戏

很长的操作的东西,可以考虑一下什么东西是不变的。

我们黑白染色,每次同时给相邻的 , 则显然有黑白块之间的差不变。

考虑最终状态全部相同,设最后全是 ,则有:

然后初中知识分讨一下。

可得定值

无解

这说明 全都合法,但显然答案关于 递增,所以二分。

最后只剩如何 check ,这东西显然随便流,太好建模,不讲了。

H 线段树

一道很强大的 DS 啊!感觉无愧这个 3000 分。

本题第一个难点是对于这个询问操作的转换。

我们可以观察出这样一个性质:

假如一个区间 ,包含要查询的点 ,若 与他有交,则 为将两个区间并起来,原数组上新区间的max。

也就是说,我么可以通过区间合并,将询问转化为原序列上的一段区间 max

这个问题是平凡的。

但是!

我们发现将两个区间交换,其答案是不同的!换句话说,这个合并只能是从后往前有顺序的合并。

这就解释了为什么,你做区间合并时不能直接一颗主席树拍上去将整个 合并。
(我一开始的想法)

既然只能从后往前合并,那就得先找出 中最后一个包含 的区间

这个问题不可持久化的话,可以区间覆盖+单点查询max

可持久化的问题在于 max 不可减,但实际上仔细想想。

的答案与求 的答案效果是一模一样的!如果答案求出来 一定无解,否则就是 的答案。

这是此题第二个妙的地方。

那么已经求出最后一个区间 ,接下来要做的就是快速往前合并区间。

此题第三个妙的地方,这个过程实际上并不那么复杂,我们发现这个合并区间的操作本身就是左右可并的。

那我们如果求出每个区间与前一个有交的区间合并,我们就可以 倍增 的把所有前缀区间合并起来。

而带修很简单,在普通线段树上随意修改一下即可。

所以我们就用一颗(单点修改,区间取max)普通线段树 + (区间推平,单点路径max)主席树 + 倍增 解决了此题。

十分的妙!值得回味。

贴下代码。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. inline int read() {
  4. int x = 0;
  5. char c = getchar();
  6. bool f = 0;
  7. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  8. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  9. return f ? -x : x;
  10. }
  11. const int maxn = 1e5 + 5, INF = 1e9 + 7;
  12. int n, m, Q;
  13. int Val[maxn], Root[maxn], L[maxn][25], R[maxn][25];
  14. struct Quer {
  15. int l, r;
  16. }Que[maxn];
  17. namespace Sigment_Tree1 {
  18. struct node {
  19. int l, r;
  20. int maxx;
  21. }Tree[maxn << 2];
  22. #define lc(i) i << 1
  23. #define rc(i) i << 1 | 1
  24. inline void Push_Up(int i) { Tree[i].maxx = max(Tree[lc(i)].maxx, Tree[rc(i)].maxx); }
  25. inline void Build(int i, int l, int r) {
  26. Tree[i].l = l, Tree[i].r = r, Tree[i].maxx = INF;
  27. if(l == r) { Tree[i].maxx = Val[l]; return ; }
  28. int mid = (l + r) >> 1;
  29. Build(lc(i), l, mid), Build(rc(i), mid + 1, r);
  30. Push_Up(i);
  31. }
  32. inline void Update(int i, int l, int r, int x, int num) {
  33. if(l == r) { Tree[i].maxx = num; return ; }
  34. int mid = (l + r) >> 1;
  35. if(x <= mid) Update(lc(i), l, mid, x, num);
  36. else Update(rc(i), mid + 1, r, x, num);
  37. Push_Up(i);
  38. }
  39. inline int Query(int i, int l, int r, int ql, int qr) {
  40. if(l >= ql && r <= qr) return Tree[i].maxx;
  41. int mid = (l + r) >> 1, maxx = 0;
  42. if(ql <= mid) maxx = max(maxx, Query(lc(i), l, mid, ql, qr));
  43. if(qr > mid) maxx = max(maxx, Query(rc(i), mid + 1, r, ql, qr));
  44. return maxx;
  45. }
  46. #undef lc(i)
  47. #undef rc(i)
  48. }
  49. namespace Sigment_Tree2 {
  50. struct node {
  51. int ls, rs;
  52. int Tag;
  53. }Tree[maxn * 100];
  54. int tot = 0;
  55. #define lc(i) Tree[i].ls
  56. #define rc(i) Tree[i].rs
  57. inline void Copy(int &i, int v) { i = ++tot, Tree[i] = Tree[v]; }
  58. inline void Push_Up(int i) { Tree[i].Tag = max(Tree[lc(i)].Tag, Tree[rc(i)].Tag); }
  59. inline void Update(int &i, int v, int l, int r, int ql, int qr, int num) {
  60. Copy(i, v);
  61. if(l >= ql && r <= qr) { Tree[i].Tag = num; return ; }
  62. int mid = (l + r) >> 1;
  63. if(ql <= mid) Update(lc(i), lc(v), l, mid, ql, qr, num);
  64. if(qr > mid) Update(rc(i), rc(v), mid + 1, r, ql, qr, num);
  65. }
  66. inline int Query(int i, int l, int r, int x, int Tag) {
  67. Tag = max(Tag, Tree[i].Tag);
  68. if(l == r) return Tag;
  69. int mid = (l + r) >> 1;
  70. if(x <= mid) return Query(lc(i), l, mid, x, Tag);
  71. else return Query(rc(i), mid + 1, r, x, Tag);
  72. }
  73. }
  74. int main() {
  75. // freopen("H.in", "r", stdin);
  76. // freopen("H.out", "w", stdout);
  77. n = read(), m = read(), Q = read();
  78. for(register int i = 1; i <= n; ++i) Val[i] = read();
  79. Sigment_Tree1::Build(1, 1, n);
  80. for(register int i = 1; i <= m; ++i) {
  81. Que[i].l = read(), Que[i].r = read();
  82. Sigment_Tree2::Update(Root[i], Root[i - 1], 1, n, Que[i].l, Que[i].r, i);
  83. }
  84. for(register int i = 1; i <= m; ++i) {
  85. L[i][0] = Sigment_Tree2::Query(Root[i - 1], 1, n, Que[i].l, 0);
  86. R[i][0] = Sigment_Tree2::Query(Root[i - 1], 1, n, Que[i].r, 0);
  87. }
  88. for(register int i = 1; i <= 20; ++i)
  89. for(register int x = 1; x <= m; ++x) {
  90. L[x][i] = L[L[x][i - 1]][i - 1];
  91. R[x][i] = R[R[x][i - 1]][i - 1];
  92. }
  93. while(Q--) {
  94. int op = read();
  95. if(op == 1) {
  96. int x = read(), num = read();
  97. Sigment_Tree1::Update(1, 1, n, x, num);
  98. }
  99. else {
  100. int l = read(), r = read(), x = read();
  101. int End = Sigment_Tree2::Query(Root[r], 1, n, x, 0);
  102. if(End < l) printf("%d\n", Sigment_Tree1::Query(1, 1, n, x, x));
  103. else {
  104. int Lpos = End, Rpos = End;
  105. for(register int i = 20 ; i >= 0; --i) {
  106. if(L[Lpos][i] >= l) Lpos = L[Lpos][i];
  107. if(R[Rpos][i] >= l) Rpos = R[Rpos][i];
  108. }
  109. l = Que[Lpos].l, r = Que[Rpos].r;
  110. printf("%d\n", Sigment_Tree1::Query(1, 1, n, l, r));
  111. }
  112. }
  113. }
  114. return 0;
  115. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注