数据结构杂题-nyh

记忆

首先 Trie 树整体 \(\pm 1\) 是容易的,只需要从低位插入,然后一直交换左右儿子然后往左/右递归即可。整体异或也是容易的,只需要打 tag,处理时判断自己这一位需不需要翻转左右儿子即可。

查一个数在插入后若干次操作后的值,记录这个数插入后的点编号,从这个点开始不断访问父亲即可知道自己的值。这里记得先跳到根 pushdown

先考虑向上走的部分,在 \(u\) 这个节点插入,在 \(lca\) 位置取出,为避免可持久化,进入某个儿子时先整体减,整体异或,出来时整体加。

回溯时做插入,然后整体异或,接着做查询。

再考虑向下走的部分。在 lca 在 \(v\) 方向的儿子处加入,在 \(v\) 处查询。

先加入接着整体异或,然后查询,最后进入子树。

进入子树时整体加,退出子树时整体减。回溯时整体异或。

对树递归处理向上向下路径的方法

JOISC2021 J 聚会

做一点分析。显然合法重心是一条链。当 \(i\) 为奇数时显然答案为 \(1\)

同时设这条链是 \((u,v)\),设 \(s_1\)\(u\) 远离 \(v\) 方向的子树大小,\(s_2\)\(v\) 远离 \(u\) 方向的子树大小。

会对 \(2,4,\dots 2\min(s_1,s_2)\) 的答案对 \(dis(u,v)\)\(\max\)

首先点分治做法是容易的。只需要维护以 \(s\) 为下标 \(d\) 为权值的后缀和即可。

然后考虑一些更需要脑子的做法。

  1. \(u,v\) 没有祖孙关系,显然就是 \(\min(sz_u,sz_v)\)
  2. \(u\)\(v\) 祖先,显然就是 \(\min(n-sz_u+1,sz_v)\)

考虑以树重心为根,特别处理树重心,则 \(n-sz_u+1\ge \frac{n}{2}\ge sz_v\),因此 \(\min(n-sz_u+1,sz_v)\) 等效为 \(\min(sz_u,sz_v)\)

所以所有的限制都是 \(\min(sz_u,sz_v)\)

因此按 \(sz\) 从大到小加入点,问题变为动态维护树直径长度。

利用重心为树根,解决关于不同方向 \(sz\) 的限制

The closest pair

注意到所有数字不相同,这启发我们调和级数。

枚举 \(a_i\),枚举 \([ta_i,ta_i+a_i-1]\) 的范围,对这个范围内的数字算支配区间。

设有两个数 \(x,y \in [ta_i,ta_i+a_i-1],pos_x< pos_y<i\),则 \(x\) 有用,至少 \(w(x,a_i)<\min(w(y,a_i),w(x,y))\)

\(w(x,a_i)<w(y,a_i)\implies x<y\)

\(w(x,a_i)<w(x,y)\implies x\bmod a_i<y\bmod x<\frac{y}{2}\implies x<\frac{y+ta_i}{2}\)

所以可以看到,取值区间 \([ta_i,r]\),每次找到一个值 \(y\),都会变成 \([ta_i,\frac{(y+ta_i)}{2}]\) 也就是缩小一半,所以最多选出 $\log $ 个元素。

放宽限制,每次找到在取值区间范围的小于 \(i\) 的最大位置 \(x\),将其加入决策集合即可。

这一步可以使用线段树,以值为下标,原数组下标为值,查区间最小值即可。

右边是同理的。最后询问就做一个扫描线即可。

\(O(n\log ^3n)\),但远远跑不满。

有效模运算至少减少两倍,倒转权值与下标,查找某个范围内最接近某个点的位置

P3687 仙人掌

判断初始状态是否合法是容易的。合法后断掉不能再被覆盖的边,变成了在一棵树上,加入若干非树边,使得每条边最多位于一个环上的方案数。不能加入重边。

不妨设 \(dp_{i,0/1}\) 表示子树 \(i\) 合并后,能否向上连的方案数。

这是为了避免重边。

转移:转移到 \(dp_{i,0}\) 的情况,就是

  1. 有些子树不连出去,就连在 \(i\) 上,或者不连,方案数 \((dp_{j,0}+dp_{j,1})\)
  2. 有些子树要连出去,这时候不需要顾忌重边,可以从 \(j\) 开始,还是 \((dp_{j,0}+dp_{j,1})\)

转移到 \(dp_{i,1}\) 的情况是类似的,不过两者分别要求第二类子树有偶数/奇数个,同时将这些方案数乘起来后需要一个分配系数。

设有 \(c\) 个儿子,\(dp_{i,0}\) 需要乘上 $\sum_{i=0}^{2i\le c}{n\choose 2i}(2i-1)(2i-3)\dots $,如果是 \(dp_{i,1}\) 需要乘上 $\sum_{i=0}^{2i+1\le c}{n\choose 2i+1}(2i+1)(2i-1)\dots $。

最后把每个连通块方案乘起来即可。

别忘记分配配对方案

P8923 Many Minimizations

考虑原问题。

显然有 dp:\(f_{i,j}=(\min_{k\le j}f_{i-1,k})+|j-a_i|\)

显然,加下凸函数,前缀 \(\min\),有凸性,考虑使用 slope trick 优化。

开一个大根堆,每次执行如下操作:

  1. 加入两个斜率转折点 \(a_i\)
  2. 将斜率为正的转折点删掉

考虑到,每次会使得最大斜率增加 \(1\),则意味着每次只会删最大的转折点,那么操作可以描述为:

开一个大根堆,每次先加入两个 \(a_i\),然后弹出堆顶。

答案嘛,自然 \(b_i\) 就是每次弹出的堆顶排序后的结果了。(这个位置在斜率为零的段的最右侧,取了准没错)。

也就是设 \(max_i\) 为每次弹出的值,答案表达为 \(\sum max_i-a_i\)

如何求这个值呢?

所有情况的 \(\sum a_i\) 和是容易的,显然是 \(n·m^{n-1}·\frac{m(m+1)}{2}\)

那么 \(\sum max_i\) 怎么算,考虑转化为 01 序列的问题,枚举一个 \(v\)\(1\sim m\),求出所有情况下有多少个 \(max_i\) 不小于 \(v\)

可以看做每次加入两个 \(0/1\),然后每次删掉最大值(有 \(1\) 就删 \(1\),没 \(1\) 就删 \(0\)

可以设 \(dp_{i,j,k}\) 表示,填完了 \(a_{1\sim i}\),当前还有 \(j\)\(1\)\(a\) 中有几个数是 \(1\)

有两类转移:

  1. \(dp_{i,j,k}\to dp_{i,\max(0,j-1),k}\)
  2. \(dp_{i,j,k}\to dp_{i,j+1,k+1}\)

最终 \(\sum max_i-a_i\) 总和就是

\[\sum_{v=1}^m\sum_{k=1}^n(m-v+1)^k(v-1)^{n-k}\sum_{j=0}^ndp_{n,j,k}·(k-j) \]

\(dp_{n,j,k}\) 怎么求?

观察一下可以发现,首先 \(j\) 的总增量是 \(k\),则总减量为 \(k-j\)。剩下的 \(n-(k+(k-j))\) 个位置哪里去了呢?在这些位置时 \(j=0\)

也就是说,我们要求的是一条从 \((0,0)\to(n,j)\),每一步位移有 \((1,1),(1,-1)\) 两种选择,当在 \(x\) 轴上的时候第二种选择变成 \((1,0)\)。使用了 \(k\)\((1,1)\)\(k-j\)\((1,-1)\)\(n-2k+j\)\((1,0)\) 的路径条数。

由对称法,这个可以等效为 \((0,0)\to (n,j-(n-2k+j))\),路径最低点 \(y\) 坐标为 \(-(n-2k+j)\) 的方案数。

使用反射容斥可以得出最低点坐标高于 \(-n+2k-j\) 的方案数是 \({n\choose k+j}-{n\choose k-j}\)

恰好经过的就是 \({n\choose k-j}-{n\choose k-j-1}\)

这里注意了,这里需要满足 \(n-2k+j\ge 0\),否则可能出现路径本身非法但组合数算出来有值的情况。

那么可以算出一个 \(g_k\) 表示 \(\sum_{j=0}^ndp_{n,j,k}(k-j)\)

答案可以写为:

\[\sum_{k=0}^ng_k\sum_{v=0}^{m}(m-v)^kv^{n-k} \]

\(v\) 看做变量,二项式展开,化简后可以得到一个 \(n\) 次多项式,带入每个 \(v=0,2,3\dots m\) 计算答案,由于系数已知,相当于算自然数等幂和,平方求解即可。

code

\(O(n^2)\)

P5609 对数据结构的爱

这很神奇了。考虑使用线段树维护。

显然有一个性质是初始值越大,则其减掉 \(p\) 的次数不会变少。

\(f_{x,l,r,i}\) 为线段树节点 \(x\) 所负责的区间 \([l,r]\),减掉 \(i\)\(p\) 的最小初始值。

初始化 \(f_{leave,i,i,0}=-\infty,f_{leave,i,i,1}=p-a_i\)

考虑左右子树做卷积合并。已知 \(f\) 后这个问题是容易的(线段树,先递归左边再递归右边,访问到完全包含的一段时直接做 upper_bound,并结合区间和就可以知道 result 变成了多少)。

\[f_{x,s}=\min_i\lbrace\max(f_{lc,i},f_{rc,s-i}-s_{lc}+ip)\rbrace,f_{lc,i+1}>f_{rc,s-i}-s_{lc}+ip \]

注意到 \(f_{x,i}+p\ge f_{x,i+1}\implies w(i,j)\le w(i+1,j-1)\)

因此可以使用双指针,初始化让 \(i=j=0\),每次优先移动 \(j\),移动不了后移动 \(i\) 即可。

由这个性质推知双指针解法,很关键

QOJ6119

首先我们需要知道如何去计算最小次数。将 ( 视作 \(1\),将 ) 视作 \(-1\)

显然在开头添加 ) 和在结尾添加 ( 的个数是确定的,根据和推知。

接着对于一个左右括号个数相同的串,又该如何求呢?

Deepseek 告诉我们,做前缀和后,对于每个 -1 的位置,若其对应的前缀和是负数,答案加上其绝对值。

考虑证明:

一个显而易见的贪心结论是:用栈扫一遍,若当前是左括号,入栈,否则检查栈是否为空,若为空,将后面最近的左括号提到这个右括号前面。

如何计算次数呢?考虑计算每个右括号被多少个左括号前移跨过即可。

这个次数显然就是该位置若前缀和为负数,则为其绝对值,否则为零次。

问题转化为:给定一个由 \((1,-1)\) 构成的序列,支持以下操作:

  1. 区间乘 \(-1\)
  2. 取出区间 \([l,r]\),求前缀和,拿出每个 \(-1\) 位置对应前缀和 \(s_i\),将 \(\max(0,-s_i)\) 求和。

这样的一个操作,放线段树上感觉很有难度,感觉不弱于区间 \(\ge k\) 的数字求和的问题。

因此使用对数数据结构至少是个两个,考虑分块。

区间乘 \(-1\) 的操作可以提前预处理一遍乘 \(-1\) 后的结果,这样只需要打个 tag 表示现在真实情况哪个版本。

考虑你怎么算答案。

本质上是一个给定 \(k\),求所有 \(\le k\) 的数字和的问题。(这里如果原位置是左括号不参与统计答案)。

分块维护此类问题一般用桶算了之后求个前缀和完事,因为有效值范围只有 \([-block,block]\)\(block\) 是块长。

这样就做完了,修改和查询散块直接暴力就行了。散块改完直接暴力重构。

posted @ 2025-08-13 21:57  spdarkle  阅读(16)  评论(0)    收藏  举报