数据结构 泛做

享受一个人在机房里面调 DS 的感受

感觉有些处理方式是很妙的

长链剖分

组合

f[x][i] 为叉的下长减上长为 i 的方案,g[x][i] 为距离其长度为 i 的点数

直接转移是 Θ(n2) 的,使用长链剖分进行优化

长链剖分将重儿子的定义改为最长链的儿子,一个性质是树上所有长链的总和为 n

转移的时候每个长链的儿子显然可以继承父亲的信息,另一个令人耳目一新的操作是使用数组分配下标的方式:先开一个长度为 2n 的数组,每次 f,g 直接分配,儿子的数组 f[son[x]]=f[x]+1,g[son[x]]=g[x]+1

剩下的都是 dp 的转移了:

For(j,0,len[t]-1){
      ans+=g[t][j]*f[x][j+1]+f[t][j+1]*g[x][j];
     if(j) f[x][j-1]+=f[t][j];
}
For(j,0,len[t]-1) f[x][j+1]+=g[t][j]*g[x][j+1],g[x][j+1]+=g[t][j];

Link Cut Tree

THUWC2017 在美妙的数学王国中畅游

想到不联通显然 LCT,那么变成抄式子,给了泰勒展开那么维护若干项即可:

考虑 sin(x) 的泰勒展开:

f(x)=sin(x),f(x)=cos(x),f(x)=sin(x),f(x)=cos(x),f(x)=sin(x)

那么复合函数求导数得到:

(f(g(x)))=f(g(x))×g(x)

所以套上去就得到了 sin(ax+b) 的泰勒展开的式子,也就是乘上 a 的若干次方

考虑 eax+b 的展开

f(x)=ex,g(x)=a(x)+b,还是能得到 (f(g(x)))

(f(g(x)))=f(g(x))×g(x)=af(g(x))

多求导就是多乘 a

那么套上 Taylor 展开的式子,维护每次的系数和,剩下的都是「模板」LCT

所以这题的收获是 Taylor 展开科技:f(x)=f(x)×xii!

ZJOI2016 大森林

考虑题设可以把它们都离线之后统一处理,那么 trivial 地用扫描线处理操作

也就是每棵树先长出来最终形态再回答询问

对于生长一个节点,直接差分,最后统一做即可

而对于更换生长点,考虑建立若干虚点表示操作,但是没有点权(真正生长出来的点有点权,为了求 dis

具体而言,一开始求最终方案的时候

修改 l,r 的生长点,先对存在 x 号点的区间取交集

之后让新点和原来的生长点连边,再和新的生长点连边

在最后的统一处理中把虚点切掉/连上即可

平衡树

【WC2016】鏖战表达式

水高分容易 AC

用可持久化 fhq treap 做其实十分显然,所以 80 ptstrivial

mathew99 的博客里面给出了卡掉的构造:先来一堆 ord=1,再来一堆 ord=2,一次类推,这样这个算法的复杂度到了 klogn

而正解用了随机化:对于 fhq treap 的合并,这样写:

if((ord[l]==ord[r]&&rand()%(sz[x]+sz[y])<sz[x])||ord[l]<ord[r]) balabala...
else balabala....

这样单次复杂度期望 O(k+logn)

证明?Itst说有2019/2018的论文,然而论文网址废掉了

记录两个 fhq treap 的手残的地方:

(1) split 第二个的时候记得是 split(x,size,x,z)

(2) 可持久化的时候 merge 写的可持久化后的点

Codeforces702F T-shirt

一开始得到一个二分的做法,但是发现这个没有单调性,所以就放弃了

换一个角度,fhq treap 对每个人的获得量和当前剩余的钱数进行维护

对于每个货物,二分得到买得起它的子树,打钱数减少和答案增加的标记

对于权值减少后的重复,也就是 costv2costv 暴力插入平衡树里面,剩下的因为肯定还是权值大的部分,直接合并

每次插入会导致 v 减少一半,所以复杂度是合法的

这个标记下方要注意,尤其是最后输出答案的时候要遍历全树下放标记

【Hdu6087】Rikka with Sequence

维护原始的平衡树来对付 3 操作,每次拆出来对应的区间合并就行

对于 2 操作,考虑 k<rl+1 的时候这个区间是会被不断复制的

那么把区间分裂出来,然后倍增出来每段的树,最后统一合并区间即可

这里还有一个 trick 是定期重构,具体而言: if(tot<M-N/3) rebuild();

cdq 分治

Luogu4655

一万年没写过斜率优化了,所以重学了斜率优化

dp 式子是 trivial 的,如下:

fi=(minj<ifjsj+(hihj)2)+si1

fi=(minj<ifjsj+hj22hihj)+si1+hi2

如果 j>k 同时

fjfk+sk+1sj+hj2hk2hjhk2hi

那么 j 的决策比 k

但是 hi 不保证满足任何条件,所以我们并不知道这要维护个啥凸包

所以 cdq 分治来解决动态凸包的离线维护:

按照 cdq 分治优化 dp 的原理,先处理左侧的 f 区间内左边对右边的贡献,最后处理右边的答案

对于当前分治区间 [l,r],先按照 id 排序,处理左边的答案,回溯之后把左边的凸包建出来,这样满足转移都是有效的

然后把右边的点按照 x 排序进行转移即可

复杂度 O(nlogn) 或者用 sort 就多个 log

具体实现的时候也可以先把所有点的 w 都扔到 f1 上然后每次转移可以方便一点

Luogu4849

然后就写了个 cdqcdq 的模板题……

cdq 的原理,考虑 sort 掉若干维之后剩下的维中左对右的贡献

处理低维 cdq 的时候我们本质上是把左侧的 a 只转移给右侧的 a

那么先做一次 cdqb 也同样操作一下,标记上是 L/R

所以 aL,bL 的才可以添加进入 BIT 同时 aR,bR 的才可以查询

再套上个 cdq 支持多维偏序即可

代码其实 trivial ,想法确实有意思

Luogu6007

fx,y 表示到 (x,y) 的最小步数

转移是 trivial 的,fx,y=min(min((fx1,y,fx,y1)+1,fp,q[qx,qy])

既然是个 cdq 的题目,那么总该有点 cdq 的求最大值的样子

所以考虑转化成最多节省了多少步,则答案为 2mfm,m

每次支持查询 (1,1)(x1,y1) 的最大的 f 即可

稍微转化一点的 cdq

目前不会 BITΘ(nlogn) 的做法

虚树

HNOI2014 世界树

得到一个朴素的 dp,设 fx 表示 fx 的归属,那么 bfs 可以得到

时间复杂度 O(nq)

但是这样虚树的数据范围就没用了,发现其实可以分割链的贡献

不难进行若干次 dfs/dp 求出来每个虚树上的点的归属,设为 bel[x]

对于一条链 (u,v) 分如下的情况讨论

(1) 如果 bel[u]=bel[v] 那么直接找到 uv 方向上的儿子 s,贡献是 sz[s]sz[v]

(2) bel[u]bel[v] 二分端点,sz 的贡献并不难得出来

(3) 虚树上的点不在虚树上的子树的贡献,显然应该贡献给 bel[u]

时间复杂度 O(nlogxn)

HEOI2014 大工程

建虚树,然后 dp 考虑每个虚树上的链的贡献即可

注意:每个链的经过次数是 sz[x]×(msz[x])

点分治

分岔路口

显然先跳然后走会比较优秀,那么这个做法对两点的距离没有要求,而且答案一样

所以计算一下这个分界然后判断即可,写出在对于一个范围:E=n+Sumnum

num 表示在范围内的点的个数,Sum 表示这些点的距离和,这部分可以使用点分治/树

Id 函数和 F(x)=n+SumNum 都是连续的,交点必然只有一个,所以可以使用二分求交点

实现的时候每次让 ans 对二分得到的期望取 min 保证正确性

luogu4115

本来想套 ZJOI2007 捉迷藏的线段树维护直径的做法,但是交了几次发现不行

其实也就是因为那个题的边权都是正的,这题从下往上传递信息的时候会挂掉

那么还是去想点分治

每个点存到点分树夫亲的距离 d[x] 和 所有点分树儿子的最大值

具体维护时,用第一个堆来维护第二个堆(这里看了题解)

当然需要支持可删除堆来代替 set ,那么我又复习了如何写可删除堆

想了半天才懂,懂了就好说了

CF990G

大力点分治,每次直接 O(n2) 合并子树信息即可

不太清楚为啥能过,但是过掉了

看题解说 gcd 有收敛性?

Luogu2664

如果当前点 x 的颜色是从根到之的第一次出现的,那么它对于根的其它子树的答案有 sz[u] 的贡献

那么我们现在仅考虑如何求得跨过 root 的答案

考虑记在当前分治重心的总权值和为 sum ,那么其对于 ans[x] 的贡献要减掉 x 子树里面的点的贡献

接着考虑在 xroot 的路径上的点,设个数为 num 不难得到贡献应该是其它子树里面的和:(sz[root]sz[x])×num

大体思路并不复杂,魔鬼永远在细节:分治重心的处理和子树内部颜色的重复

需要想清楚,想清楚了并不复杂

归总,写很多个 dfs 就行了


注意!!点分治找重心的时候记得清空 f[x]

Codeforces150E Freezing with Style

首先把中位数都离散化下来,然后点分治的过程中进行二分

以上都是从 WC2010重建计划 中套出来的 trivial 套路

显然是考虑对于每个分治重心维护跨过其的最大的两个链进行合并

中间套个单调队列维护两个信息就行了

貌似单调队列写了一万年,为啥都快省选了单调队列这种东西还要调……

Luogu3714

点分治,接着考虑如何合并两个子树的信息

按照上个题的思路,两个不同颜色的合并需要减掉对应的代价

具体而言,对于每个扫出来的子树,合并长度为 i 的路径信息就查 [L-i,R-i] 最大值

所以搞俩线段树,颜色不一样的时候线段树合并就好了

动态 DP

如何求带修改的最大独立集?

把转移分成两个,即 f[i][0/1] 表示(不)选择当前点的答案

同时 g[i][0/1] (不)考虑重儿子当前点的答案

这东西可以写成一个矩阵乘法的形式

那么每次被更改的 f,g 数组构成一条链,对矩阵开线段树维护即可

卡常方式貌似是用 LCT 或者循环展开

有效的是每个重链开线段树来降低 query 的复杂度

posted @   没学完四大礼包不改名  阅读(124)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示