数据结构小记

只会做傻子题了。

CF576E

傻子题。线段树分治,对于每条边相邻的两次修改 \(t_1,t_2\),把 \([t_1+1,t_2-1]\) 时刻内这条边的颜色打到线段树上,维护 \(k\) 个可撤销扩展域并查集即可。

P4690

没那么傻的题。如果是单点赋值的话就是动态二维数点,CDQ 分治直接做就行了。然后发现区间赋值只会产生 \(\mathcal{O}(n+m)\)\(pre\) 的修改,然后就做完了。ODT 维护颜色段,再对每种颜色维护所有段就可以维护 \(pre\) 了。

P5631

傻子题。把所有边权为 \(x\) 的边扔掉就可以 \(\mathcal{O}(n\alpha(n))\) check。考虑分治优化,分治到 \([l,r]\) 时保证 \([1,l-1]\) 的边都加了。然后把 \([mid+1,r]\) 的边全部加进来,连通答案就是 \(l\),否则递归解决,可撤销并查集随便维护。

P1527

入门题。整体二分,用二维 BIT 维护。

P3769

四维 LIS。先按 \(a\) 排序,CDQ 分治,两边按 \(b\) 排序,双指针一下转成动态二维偏序,再套一个 CDQ 分治就行了。

P3527

入门题。整体二分,树状数组维护。

P3733

傻子题。根据经典结论,要用线性基维护所有非树边对应环的异或和。线段树分治,可撤销并查集随便维护,线性基也是很好撤销的。

P4175

入门题。整体二分,树状数组维护 DFS 序。

CF1140F

入门题。建二分图,对于一个连通块,如果里面有 \(x\) 个左部点,\(y\) 个右部点,那就有 \(xy\) 的贡献。线段树分治维护。

P3309

经典题。线段树维护凸壳,尾插时只重构那些恰好以该点为右端点的区间。

CF710F

入门题。二进制分组暴力合并 AC 自动机。

P3810

板子。CDQ 分治,树状数组维护。

QOJ3684

把条件转化成 \(x^2+y^2\leq \min\{2a_ix+2b_iy\}\),CDQ 分治后建出凸包维护。卡精度,傻逼。

P2617

入门题。整体二分,树状数组维护。

P5787

板子。线段树分治,可撤销扩展域并查集维护。

UOJ191

有点牛的题。尾删使得 P3309 的做法会死。考虑一种比较高妙的重构方法,在尾插时,对于每个恰好以 \(x\) 为右端点的节点 \([l,r]\),考察 \([l,r]\) 在当前层中的前一个节点 \([l',r']\),若其需要被重构则执行重构。叶子需要特殊处理。这样一个节点被重构后,至少要 \(\mathcal{O}(len)\) 次操作才会再次重构,所以均摊下来复杂度是整体 \(\mathcal{O}(n\log{n})\) 的。查询时依旧会拆成 \(\mathcal{O}(\log{n})\) 个区间,所以询问复杂度还是双 \(\log\) 的。卡细节、卡空间,傻逼。

P6240

入门题。猫树分治维护背包。

SPOJ9576

傻子题。线段树分治,可撤销并查集维护。

P14368

难难难。\(q=1\) 时有 \(\mathcal{O}(n\log{n})\) 的分治做法。考虑倍增值域分块,块内的两个端点 \(l,r\) 满足 \(2^i\leq l\leq r<2^{i+1}\),即 \(2l>r\)。此时对于一个包含 \(x\) 的合法区间,左右端点中至少有一个到 \(x\) 的距离 \(\leq l\)。这样假如枚举左端点 \(L\) 满足 \(x-l+1\leq L\leq x\),那么所有以 \(L\) 为左端点的合法区间都可以贡献到 \(x\) 上,可以直接预处理出 \(f_L=\max\limits_{L+l-1\leq i\leq L+r-1}\{s_i-s_{L-1}\}\),然后单调队列算滑动窗口最大值。右端点同理。于是我们可以 \(\mathcal{O}(n)\) 解决 \(2l>r\) 的 case。对每个整块预处理答案,建出 ST 表,询问时散块直接线性做,复杂度就是 \(\mathcal{O}(n\log{n}\log\log{n})-\mathcal{O}(n)\)

CF678F

傻子题。线段树分治,可撤销李超线段树维护。

P5445

有点牛。考虑一个 \(0/1\) 矩阵 \(A\),其中 \(A_{i,j}\) 表示当前 \(i,j\) 是否连通。用 set 维护一下 \(0\) 的位置,可以发现每次修改相当于给矩形赋值 \(0/1\)。考虑怎么处理前缀时刻的答案,贡献提前计算,把 \([t+1,q]\) 时刻的 \(0/1\) 贡献视为当前状态,操作变成矩形加或减 \(q-t\)。询问时若 \(i,j\) 连通则把 \(q-t\) 扣掉即可。CDQ 分治简单维护。

P4246

SPOJ9576 没区别。

LOJ121

SPOJ9576 没区别。

CF1045G

傻子题。按 \(r\) 从小到大排序后只需要保证左边的能看到右边的。CDQ 分治,那就变成了二维数点。可以实现的优雅一点,两边按 \(q\) 排序,双指针,树状数组维护所有合法的 \(x\)

P4390

入门题。CDQ 分治,树状数组维护。

P4849

P3769 一样。

CF938G

P3733 一样。

P4169

入门题。CDQ 分治,分讨拆绝对值,树状数组维护。

Gym102354B

不难。调和一下,只用考虑怎么求出 \(c_1\)\(\max\) 不好维护,考虑二分答案转成求和,也就是转化成求

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n[\gcd(i,j)=1][|a_i-b_j|\geq k] \]

莫反变成

\[\sum_{d=1}^n\mu(d)\sum_{i=1}^{n/d}\sum_{j=1}^{n/d}[|a_{id}-b_{jd}|\geq k] \]

把所有 \(a_{id},b_{id}\) 排序,那么后面的两个 \(\sum\) 可以直接双指针求出。这样整体时间复杂度为 \(\mathcal{O}(n\ln^2n\log{V}\log{n})\),无法承受。

考虑优化排序的部分。可以发现最外层枚举 \(k\) 时,内部相当于给 \(d\) 乘了个 \(k\)。完全可以提前 \(\mathcal{O}(n\ln{n}\log{n})\) 求出对于所有 \(d\)\(a_{id},b_{id}\) 排序后的结果,时间复杂度降至 \(\mathcal{O}(n\ln^2n\log{V})\),可以通过。

CF848C

傻子题。拆成 \(i-pre_i\) 之和,动态二维数点,CDQ 分治维护。

P8569

神题。

容易得到一个 \(\mathcal{O}(n\log{V})\) 的做法。拆位,对于某一个位 \(i\),一个区间在该位上有贡献当且仅当其中存在 \(1\)。考虑从小到大右端点 \(i\),设 \(f_{j}\)\(a_{1\sim i}\) 在第 \(j\) 位中最后一个 \(1\) 的位置。每次加入一个 \(a_i\) 时,若 \(\operatorname{bit}_j(a_i)=1\),则令 \(f_j\gets i\),否则不变。每个 \(i\) 会对答案贡献 \(2^i\sum\limits_{j=0}^{\log{V}}f_j\)

考虑优化。这里使用一个很牛的 trick:注意到我们的转移在维护 \(f\) 时相当于维护了 \(\log{V}\)\(\leq n\) 的数,考虑把 \(f\) 视作一个 \(\log{V}\times \log{n}\)\(0/1\) 矩阵,那么我们将这个矩阵转置,改为维护 \(\log{n}\) 个 word。

考虑原来 \(f\) 的变化怎么放到 \(g\) 上。考察一个位 \(j\) 使得 \(\operatorname{bit}_j(a_i)=1\),对于每个 \(k\),我们需要把 \(g_k\) 的第 \(j\) 位设为 \(\operatorname{bit}_j(i)\)。不难看出这相当于:枚举 \(k\),若 \(\operatorname{bit}_k(i)=1\),则令 \(g_k\gets g_k\operatorname{or} a_i\),否则令 \(g_k\gets g_k\operatorname{and} \lnot a_i\)。此时每个 \(i\) 对答案贡献 \(\sum\limits_{j=0}^{\log{n}}2^jg_j\)

P5416

击杀了,感觉不到黑。

套路地建出版本树,放到 DFS 序上考虑,显然所有点的出现区间会在 DFS 序上形成最多 \(n\) 个连续区间,DFS 的时候简单维护即可找出。

拆一下询问的式子,变成 \(x_0^2+\min\limits_{(x,c)\in S_p}\{-2x\cdot x_0+(x^2+c)\}\)。显然可以把每个点视作一条 \(k=-2x,b=x^2+c\) 的直线,对点集内的直线建出上凸壳即可查询出 \(\min\)

考虑线段树,在每个节点上维护一个 vector,每个区间会在线段树上分成 \(\mathcal{O}(\log{n})\) 个节点,把 \(id\) 插进这些节点对应的 vector 里。然后对于线段树的每个节点,把这个节点的 vector 对应的直线排序后单调栈建出上凸壳,查询时对每个经过的节点都二分一下。这样复杂度是 \(\mathcal{O}((n+m)\log^2{n})\) 的。

显然可以优化。我们提前把 \(\mathcal{O}(n)\) 个区间按照对应直线的斜率排序,这样所有插入操作完成后,每个 vector 里的对应直线就已经排好序了。询问可以同理按照 \(x_0\) 排序,把二分改成暴力走指针。这样两边复杂度都降至 \(\mathcal{O}(n\log{n})\) 了。

CF232E

直接 bitset 跑可达性是 \(\mathcal{O}(\frac{n^4}{\omega}+q)\) 的,无法承受。显然这样完全没有利用网格图的性质,考虑对 \(x\) 坐标猫树分治,每次只需处理出 \(f_{i,j,k}\) 表示能否从 \((i,j)\) 走到 \((mid,k)\)\(g_{i,j,k}\) 表示能否从 \((mid,k)\) 走到 \((i,j)\)。询问时只需要判断 \(f_{x_1,y_1}\)\(g_{x_2,y_2}\) 是否有交集。时间复杂度降至 \(\mathcal{O}(\frac{n^3\log{n}}{\omega}+\frac{qn}{\omega})\)

P4027

简单题。令 \(f_i\) 表示前 \(i\) 天最多能持有多少人民币,\(x_i,y_i\) 表示在第 \(i\) 天用 \(f_i\) 元分别能兑换多少 A 券和 B 券。解方程可得 \(x_i=\dfrac{f_ir_i}{a_ir_i+b_i},y_i=\dfrac{f_i}{a_ir_i+b_i}\)。那么转移就是 \(f_i\gets \max(f_{i-1},\max\limits_{1\leq j<i}\{a_ix_j+b_iy_j\})\)。用 P3309 中的方法维护即可。

posted @ 2025-12-05 00:52  P2441M  阅读(10)  评论(0)    收藏  举报