数据结构水题选讲

[Ynoi2011]ODT

(O(nlog^2n)) 的做法非常显然

直接把树重链剖分一下,每个点维护轻儿子的平衡树就行

但是这题 (1e6) 的数据范围使得 (O(nlog^2n)) 没那么容易卡过去(当然很多人卡过去了

考虑给一个点很多重儿子

那么若一个点有 (k) 个重儿子,修改复杂度就变成 (O(log_knlog_2n)),而查询复杂度变成 (O(klog_2n))

为了均摊我们需要 (log_kn=k ightarrow k^k=n),发现当 (k=7) 时应该是最优的

这样我们得到了一个复杂度 (O(7nlogn)) 的算法,已经足够通过这道题了

好像还有一个 (log) 的做法但是我不会也找不到讲解先咕了

[Ynoi2019]魔法少女网站

Ynoi+序列=分块

考虑把操作分块

把所有询问按照x从小到大排序,对于当前的x,把小于等于他的位置设为1,大于的位置设为0,每次x变大就把新符合的变成1

用类似链表的东西维护一下

由于需要查询区间我们考虑分块维护(因为我们需要 (O(1)) 插入,现在唯一的问题就是修改操作中会把1变成0

考虑一个套路就是插入之后按序撤销,初始让所有被修改的位置一直是0就行了

复杂度 (O(nsqrt{n}))

[Ynoi2017]由乃的玉米田

前两个操作可以显然的用莫队+bitset做,第三个可以直接莫队,我们只考虑一下第四个

第四个操作当 (xgesqrt{n}) 的时候显然可以直接在莫队询问的时候暴力做

(x<sqrt{n}) 时,我们对每个 (x) 单独考虑,直接扫一遍整个序列,对每个(r')记录最大的(l')就行了

复杂度 (O(nsqrt{n}+frac{n^2}{omega}))

[Ynoi2016]掉进兔子洞

显然的答案就是 (r_1-l_1+r_2-l_2+r_3-l_3+sumlimits_a min(cnt_1[a],cnt_2[a],cnt_3[a]))

用前几天大神讲的那个 (bitset) 实现取 (min) 的方式得到每个所需区间的 (bitset) 然后与一下就行了

得到每个区间的 (bitset) 可以直接用莫队来做(当然空间是开不下的,分多组做就行了)

CF1148H Holy Diver

对每个右端点 (r) 维护每个左端点 (l)(mex)

显然的一件事是 (mex)(l) 的增加而减小,且新加入一个 (a_r) 只会影响到之前所有 (mex=a_r) 的位置

显然的 (mex) 序列被分成若干段,且随着 (r) 的右移分界点最多增加 (n) 个,所以我们每次可以暴力的二分找出所有分界点来区间修改

考虑怎么维护答案,发现每次就是把一个 (mex) 相同的区间的 (mex) 集体改变

设该权值某个位置在时刻 (t_1) ​出现,时刻 (t_2) ​消失或询问,则该位置贡献为 (t_2−t_1+1),每次修改操作相当于区间加。

对每种权值用动态开点线段树维护一下就行了

CF704E Iron Man

考虑序列上怎么做

我们让时间为横坐标,位置为纵坐标,那么每个点相当于一条线段,我们要找的就是最小的出现相交的横坐标

考虑对横坐标做扫描线,发现如果每个时刻把存在的线段按照目前的纵坐标排序那么有交点的线段一定在某一时刻是相邻的

考虑直接用 (set) 维护,加入的时候和前趋后继求交点,删除的时候让前趋后继求交点

虽然纵坐标是变化的,但是显然的在出现交点之前线段之间相对位置不变,只要在横坐标 (ge) 目前求出的交点时退出就行了

放到树上,直接树剖一下,每个重链跑一遍就行了,复杂度 (O(mlog⁡mlog⁡n))

CF1083D The Fair Nut's getting crazy

设 L[i] 表示 a[i] 在 i 之前的最后一次出现, R[i] 表示 a[i] 在 i 之后的第一次出现

那么答案为 (sum_{lle r,l>maxL(l,r),r<maxR(l,r)}(l-maxL(l,r))(maxR(l,r)-r))

考虑扫描线,显然的对于 (r,l) 是单调的

然后把括号拆开用线段树维护一下就行了

复杂度 (O(nlogn))

原文地址:https://www.cnblogs.com/mikufun-hzoi-cpp/p/13195512.html