Ynoi 简单题记录

UPD 0524:我更了很多,但是不想放上来了。

感谢萌熊。按 tby 给的难度排,虽然我感觉这个难度不太靠谱。

先口胡吧,代码没写的可能会补。

P5610

Tag:简单线段树。

忽略 \(x=1\) 之后,暴力找到所有的 \(x \mid a_i\) 并且 \(a_i \leftarrow \frac{a_i}{x}\) 复杂度就是对的。

对于每个因数,维护其倍数出现的集合。发现我们最开始确定集合之后,只有不断删数的过程。

所以把他们拍到序列上,直接使用并查集维护即可,复杂度 \(O(n d(V) \alpha(n)+ q\log n)\)

P5609

Tag:简单线段树。

如果没有强制在线的话,可以标记插入回收+平衡树值域有交合并。看起来很魔怔。

\(f(x,l,r)\) 为,将 \(x\) 喂进区间 \([l,r]\) 中,执行这个过程后 \(x\) 会减去多少次 \(p\)

显然 \(f(x)\) 关于 \(x\) 是单调的,所以你只用维护断点。(注意到 \(f(x) \ge f(x+1)-1\),所以这个东西比较连续)。

考虑用线段树维护(无修改,其实是静态线段树。这个东西不太好猫树(空间寄),所以用普通线段树)。合并区间 \([l,mid]\)\([mid+1,r]\) 的时候,可以直接用 \(O(r-l)\) 的复杂度扫描一遍。

总体复杂度 \(O(n \log n + m \log^2 n)\)

P5309

Tag:简单根号分治。

对于 \(x\) 比较大的情况,可以暴力修改,使用值域分块做到 \(O(\sqrt n)\) 查询和 \(O(1)\) 修改,这一部分总体复杂度为 \(O(n B)\)

对于 \(x\) 比较小的情况,记录 \(ad_{x,y}\)。对于固定的 \(x\)\(y\) 的一个前缀会对 \([1,r]\) 贡献 \(cnt\) 次,剩下的会贡献 \(cnt-1\) 次,使用前后缀和优化即可。

P5524

Tag:简单线段树。

考虑怎么确定一个位置的答案。对于位置 \(x\),我们倒着扫,维护每个时刻它出现的位置,如果落在了区间赋值里答案已知,否则答案就是 \(0\)

因此 \(3\) 操作最终的贡献要么是 \(0\),要么是一个确定的值。而且具体是哪个关于 \(l\) 具有单调性。直接二维数点即可。

P6018

Tag:trie 树,树上 \(1\) 邻域的处理。

对于树上 \(1\) 邻域的处理——维护每个点所有儿子的信息,修改和查询的时候往父亲上检查一下即可。

集合 \(S\) 整体加一、维护二进制信息:倒着建 trie 树,然后一路交换两个子树。

P5070

Tag:简单二维信息查询。

显然你可以在 \(O(1)\) 的时间复杂度内得出 \([l,r]\) 是否合法(注意长度要保留到 \(11\))。

对于 \(l \le L \le R \le r\),如果 \(l \neq L\)\(r \neq R\),那么 \([L,R]\) 是否合法与在原序列中是否合法情况相同,可以直接二维数点。

对于 \(l=L\)\(r=R\) 的情况,暴力做一下就行了。

P4692

Tag:STL,分类讨论。

太 tm 难写了,但是思维很简单。

P6105

Tag:STL,简单维护。

显然将所有数按照 \(C\) 取模后,计算 \(x+y < C\)\(x+y\) 最大值以及 \(x+y < 2C\)\(x+y\) 最大值。

如果只有加入操作,那么对每个数维护当前意义下的最优匹配即可。

把最优匹配当成边。发现可能会出现菊花。而删掉一个点需要把所有边都给断掉,连上新的边。这样复杂度均摊下来就不对了。

不过我们可以维护“双向最大值”。要求双方都是最优的(注意可能模 \(C\) 之后会出现两个数相同,所以在值相同的时候我们要最大化编号,这样无论插入还是删除都是严格 \(O(1)\) 的了)。

P5046

Tag:简单分块。

把当时代码里的注释放上来:

1. 如何计算块内逆序对 -> 直接暴力。 
2. 如何计算两块之间的逆序对 -> 把序拍好,然后归并,显然单根号。
3. 如何计算若干块之内的逆序对 -> 多高几个前缀和。
4. 如何计算散块的答案:
	a. 散块内部——逆序对信息有前缀后缀性,在预处理的时候加进去。
	b. 散块之间——直接归并。
	c. 散块和整块之间——记录前 i 个块有多少个 <=j 的元素 
5. 如果答案全在一个块里——先利用差分,然后发现转化为散块之间的做法。。。 
发现这个东西实际上有 O(n) 次插入和 O(n^{1.5}) 次询问,所以使用值域分块,O(n^{1.5})。

P7447

Tag:倍增分块,底层空间优化。

由于相对关系发生的变化会比较多,所以直接使用平衡树啥的不太好维护。

有一个非常牛的技巧叫做倍增分块。把值在 \([2^k,2^{k+1})\) 中的数塞到一棵线段树里面。

发现只有将一个数降级和整体打标记两种操作。

不过你会被卡空间。可以将底层按 \(\log n\) 先分一次块。由于区间询问只会访问到 \(O(1)\) 叶子,所以你这么做复杂度数对的。

P5047

Tag:莫队二次离线。

唉感觉上这个东西和 P5046 一模一样——结果发现卡空间。 /xk

其实把分块预处理的那一大坨东西直接离线下来是可行的(空间严格 \(O(n)\))。

直接莫队复杂度必然含有 \(\log\),因为无论是修改还是查询个数都是 \(O(n \sqrt n)\)

考虑二次离线。记 \(f(i,j)\) 为:前 \(i\) 个数中有多少个比 \(j\) 大。

比如我们往右移动 \(r\),给答案的增加量就是 \(f(r,r+1)-f(l-1,r+1)\)

前者可以直接做前缀和,后者看起来还是二维数点。但是修改变成了 \(O(n)\) 个,可以值域分块平衡复杂度了。空间还是线性。

P3934

Tag:扩展欧拉定理。

直接模拟即可。

nfls 之前模拟赛有一个类似题,我去我还没补。

P5048

Tag:分块。

用 5046 的方法也可以处理,但是空间不是线性的。

但是整块之间的信息是可以记录的。所以我们知道了答案的下界。

而只有散块才能让答案取到更大的值。

考虑这样一个过程:对于散块中的每一个位置,往另一个散块的方向尽量扩展(在原有答案的基础上)。发现这样扩展一次是 \(O(1)\) 的,而最多扩展 \(O(\sqrt n)\) 次,所以总复杂度为 \(O(n \sqrt n)\)

P5072

Tag:简单莫队。

原来这个东西不是对所有子序列去重吗,那很坏了。

一个数会在 \(2^{r-l+1}-2^{r-l+1-occ_p}\) 个序列中出现。

唉,出现次数这种东西,本质不同的情况只有 \(O(\sqrt len)\) 的。

所以直接算就行了。

考虑怎么精细实现。首先你需要对一个集合进行 \(O(1)\) 的插入删除并且枚举。这个看起来不太能直接做,但是发现如果要删除或加入一个元素,其加一或减一一定已经在序列中。所以可以直接维护双向链表。

然后是算 \(2\) 的幂。这个可以预处理 \(2^{\sqrt n}\) 次幂取模后 \(O(1)\) 算。

P5611

Tag:神奇分治,简单分块。

考虑本质有用的 \([L,R]\) 只有 \(O(n^2)\) 对,比朴素的 \(O(V^2)\) 优秀不少。

如果询问是 \(l=1\)\(r=n\),可以考虑这样一个分治:

  1. 将问题拆分为 \([l,mid]\)\([mid+1,r]\)
  2. 考虑每个可能的 \([L,R]\),作为子问题放入 \([l,mid]\)\([mid+1,r]\);或者找到 \([L_1,R_1]\)\([L_2,R_2]\) 作为两边极大的、属于 \([L,R]\) 的子区间。这样的 \(L_1\)\(R_1\) 可以直接双指针优化寻找做到均摊 \(O(1)\)

根据主定理,复杂度为 \(O(n^2)\)

本题中,分块后这一部分复杂度就是 \(O(n \sqrt n)\)

散块是容易处理的,复杂度 \(O((n+q)\sqrt n)\)

空间乍一看必须 \(O(n \sqrt n)\)。但是可以把所有询问离线下来,对于每个块把答案加到包含他的所有询问上。这样空间都是线性的了。

P7721

Tag:值域分治套路。

类似上一道题 P6105。显然将两个序列归并后和式的变化不好计算,所以考虑对值域进行分块。

而信息只需要返回四元组 \((ans,cnt,mn,mx)\) 表示答案,最大值,最小值。

然后合并的时候如果前一个的 \(mx\) 等于后一个的 \(mn\) 要将其删掉。

复杂度 \(O((n+q) \sqrt n)\)

P5314

Tag:简单树链剖分。

考虑进行轻重链剖分。修改的时候只修改轻儿子,重儿子在查询的时候算即可做到 \(O(q \log^2 n)\)

但是发现查询是单 \(\log\) 的,考虑平衡复杂度。每个点维护 \(B\) 个重儿子,那么轻边是 \(O(\log_B n)\) 条。修改 \(O(\log_B n \log n)\),查询 \(O(B + \log n)\)。取 \(B = \log n\) 就可以做到 \(O(q \frac{log^2 n}{\log \log n})\)。你会去写吗。

P5607

Tag:简单根号分治。

显然如果值域很小,比如 \(10^5\),你可以上 bitset。注意从理论上空间可以做到 \(O(m)\) 如果你用手写 bitset 的话。不过手写 bitset 可能太慢了。

所以分为出现次数多的数和出现次数少的数。

对于出现次数少的数,由于询问对每个数并不是 \(O(1)\) 的,所以你不能让修改对询问产生贡献。

把询问拆成 \((x_1,x_2)\)\((x_2,x_1)\),只考虑第一个集合中加入的元素比第二个集合加入该元素要早。

把每个数的插入的时刻和询问的时刻做一个归并,修改的时候往一个数组里面加即可。

P6783

Tag:KD-Tree,分块。

记住 KD-Tree 的复杂度:可以保证单次查询到的矩形个数不高于 \(O(\sqrt n)\)。对 KD-Tree 的操作可以定期重构(操作分块)。

离线扫描线,然后就在 KD-Tree 上打标记。不过打一个标记需要把它覆盖的标记都给撤销掉。

而直接暴力撤销标记,显然复杂度就是总访问的节点个数。可以证明它就是 \(O(m \sqrt n)\) 次修改。

而查询只有 \(O(m)\) 次对不对,所以用值域分块平衡一下复杂度。

P7448

Tag:二维分块。

双倍经验 P7601。

考虑莫队二次离线。那么我们会新增哪些信息呢。发现需要设每个位置上一次出现在哪里。如果 \(lst_r < l\) 那么查询 \(l\)\(r-1\) 间有多少数比 \(a_r\) 大;否则贡献一个定值。

这样就需要再 \(n \times n\) 的平面上增、减点(总个数 \(O(n^2)\)),查询一个 2-side 矩形中点的数量。这就需要用到二维分块了。

当然一般的二维上的点不能在低于 \(O(n^2)\) 的时间或空间复杂度干这个事情(至少我不会),但是这题的性质保证了所有点横坐标和纵坐标两两不同。

那么分为 \(n^{0.75}\) 的块和 \(n^{0.5}\) 两种块,类似下图的结构:

注意灰色部分是你暴力算的!这个部分根本做不了。

P5397

Tag:分块,根号分治。

根号分治做法 1:你把数分为关键的和非关键的。维护所有关键的数构成的集合,每个数到每种关键的数的距离,关键集合两两距离。合并非关键的直接归并,产生了关键的就 rebuild;合并关键的也归并并且重构;合并关键的和非关键的,对关键距离产生了区间加减的影响(区间加减数量 \(O(n)\) 所以可以用值域分块平衡)。

根号分治做法 2:我还没看。

序列分块做法:每个块对可能的 \((x,y)\) 只有 \(O(B^2)\) 个贡献。然后维护每个数在其中第一次出现,最后一次出现,两两内部最近距离,随便合并就行了。

会有一些比较麻烦的集合操作,唉反正是口胡。

P6109

Tag:猫树分治。

对询问做猫树分治。

修改如果完全覆盖直接下传,而只有 \(O(\log n)\) 个区间覆盖了但没有完全覆盖。在猫树分治的过程中随便维护一下就行。

P7710

Tag:简单根号分治,简单分块。

看到模数就想到根号分治。

先给排成 DFS 序,然后变为区间深度满足某个同余特性的点点权加 \(w\)

那你给 DFS 序分个块。散块的处理非常简单。对于模数比较小的情况,由于一共只有 \(O(B^2)\) 种,所以你可以给每个块暴力打个标记,复杂度 \(O(B^2 \sqrt n)\);对于模数比较大的情况,显然不能每个块都打标记因为你要枚举具体深度所以总的信息量是 \(O(\frac{mV \sqrt n}{B})\) 的,你可以直接把信息改成整块的单点加,查询的时候暴力计算前缀和即可。

时空复杂度都是单根号。

P4688

Tag:bitset,莫队。

所有数互不区分。所以你把整个数组排序之后,每种数在 bitset 里面贪心的往前填就行了。

空间会爆,所以你把 bitset 分成几块分别做就行了,块间独立。

P5310

Tag:哈希,平衡树。

非常简单的题目。发现 A 序列是静态的,所以我们可以把每个子区间的相对排名都用某种方式预处理出来。由于所有数都是知道的,你可以先离散化然后就不需要平衡树了。

我们维护每个排名对应的位置减去初始位置构成的序列,用哈希判断他们是否相等即可。

P6779

Tag:分块,复杂度均摊。

考虑分块。块内所有点都是在不断往上跳的。所以如果一个点到达的位置被其他点访问过了,那么它一定就没用了(至少在散块不发生变化的情况下)。

然后你就对每个块维护一个集合表示当前所有有用的点。查询还是修改用到了散块就重构,重构的时候跳祖先。虽然祖先会跳 \(O(n \sqrt n)\) 次,但是你用树链剖分维护这个过程,总的次数还是 \(O(n \log n)\) 的。

整块修改的时候打标记,然后对于可重集 S 中所有点跳父亲。如果没有用就给删掉。使用链表维护集合。复杂度 \(O(n \sqrt n)\)

还有一个问题是怎么搞空间。据说 bitset 压一压直接能过了,我也不知道。然后把每个块离散化下来也行。

P7124

Tag:树,分治。

首先,如果把子树看成 DFS 序之间的连续段话,问题就求出最小曼哈顿距离生成树。是不是可以直接套模板啊,考前可以复习一下(虽然感觉没啥用 TAT)

构造方法 1:一般莫队显然无法做到 \(O(n \log n)\)(本题一看就是这个量级)。而本题的区间有啥特殊之处呢?区间只有包含和相离关系!所以考虑分治。跨过分治中心的区间必然相互包含,所以可以 \(O(len)\) 增量构造,然后分治。可以证明,操作 \(1\) 只会用到 \(\le (1.5 \log n + 1)n\),随手过好吧!

构造方法 2:树链剖分理论告诉我们,轻子树大小和为 \(O(n \log n)\)。后面需要用到哈夫曼树,先咕咕咕。

P6774

Tag:NOI 真题,时代的眼泪。

唉先看看自己秒了多少分。

暴力做一个二维前缀和,\(24\) 分!

写一个区间逆序对,\(40\) 分!

C 就是二维数点扣掉不合法情况,\(52\) 分!

B 随便建一个区间树,然后对 LCA 产生贡献。这个东西真不好维护把,然后 dsu on tree + 树套树做到 \(\log^3\)。兄弟,要相信 5s 一定能跑过 1e5 的因为跑不满啊,\(64\) 分!

这样 Day 1 得 \(264+\) 了,而且应该还剩一些时间。这把高端局兄弟们,Day 2 只要上三位数就能进集训队!(似乎 Day 2 只要拿到 \(78\) 分就行了。这不随手打!!)

唉不对,我好像会这题正解了?那么 Day 2 只需要 \(37\) 分!无敌了兄弟们。

题解

P6019

Do Do Do work work work

P11368

Tag:线段树历史和。

lxl 在模拟赛讲解里推销了这个题。

先回顾一下这种问题怎么两只 \(\log\) 做。这是经典的楼房重建线段树(单侧递归)。具体的,就是在 push_up 中要用 \(O(\log n)\) 的复杂度实现。小粉兔在博客中提到,我们维护每个区间在只考虑左子树的情况下右子树的答案,这样信息就不需要可差分性了。

考虑这个题怎么做。如果想直接用吉司机线段树,会发现一个很头疼的问题——对区间取 max 有撤销操作。这个东西肯定是做不了的!

\(c_{i,j}\) 为第 \(i\) 个位置在时刻 \(j\) 的前缀最大值。我们求的是 \(c\) 的一个二维前缀和。

考虑交换两维坐标,将“位置”当做时间,将“时间”当做位置,用吉司机线段树实现区间取 \(\max\) 即可(因为这样所有位置都只有“增加”操作)。

注意,只有区间取 \(\max\) 的复杂是均摊 \(O(\log n)\) 的。

这是一个线段树单侧递归的例题,瞎找的

P11369

Tag:操作分块,简单图论。

考虑对时间轴进行操作分块。每个块内,把所有“关键边”都删掉之后,对其他黑边跑一边 SCC 缩点,变为 DAG 可达性问题。然后只保留 \(B\) 个有效的点或者边。

将图重标号后每次直接 BFS 即可。复杂度为:\(O(q\frac{B^2}{w} + \dfrac{qnm}{Bw})\)。取 \(B = (nm)^{\frac{1}{3}}\) 即可做到 \(O(\frac{q}{w} (nm)^{\frac{2}{3}})\)

posted @ 2025-05-01 21:31  M2GA  阅读(132)  评论(1)    收藏  举报