几道数据结构杂题

几道数据结构杂题

> 查询 Ynoi 含量中......

根号含量为 0,精神状态良好!

CF1793F / CF765F / P5926 D

区间一维最近点对。O(nlognloglogn+qloglogn)

首先一个问题就是把有用的点对找出来,使得答案一定出现在这些点对当中。在值域上按照中位数划分做一个分治,看 [l,mid],[mid,r] 之间的贡献,记 bi=|aimid|,然后贡献就是 bx+by,如果 x,y 出现在同一边也没有关系因为求的是 min.然后按照原数组的下标排序,考虑一对有用的 (i,k),必须满足 i<j<k(i,j),(j,k) 都不会比其更优,那就是 bi+bk<bi+bj,bj+bk,即 bk<bj,bi<bj

这样话,维护一个递增的单调栈,每次压入一个 bk,与其组成关键点对 (i,k)i 一定是被 bk 弹出的,以及最后一个未被弹出的那个。这样这么一次划分关键点对个数就是 O(len) 个,总的个数就是 O(nlogn) 个。时间复杂度也是 O(nlogn)

另一个思考方向是:考虑 i<j<kbi+bj<bi+bk 那么 (i,k) 无用.也就是 bj<bk,i<j(i,k) 无用,那么对于 k 寻找支配点,就直接查一下 bi<bki 中,k 的前驱和后继是啥就行(想一想,为什么这里没考虑 bi>bki)。实际上和上面那个找出来的是一样的。

然后就是 O((c+q)loglogn) 复杂度的 2-side min,这里 c 是点的个数其为 O(nlogn)

首先对 r 作扫描线,维护后缀 min 的单调栈。假如这样插入红点,查询一下红点的下标的前驱,看上面的值是不是大于红点的权值,如果大于就删掉。所以对下标开一棵 vEB 就可以了,然后查询就是查一下 l 在单调栈里的后继上挂的值。

卡空间 01 Trie B

一条边上可以有好多个数,也就是把只有一个儿子的节点给合并到一起。这样每次插入的时候最多只会新建两个节点了。需要位运算实现 O(1) 查询二进制数的 lcp.

P7476 D

没有 2 操作怎么做/fn 线段树套堆,插入的时候在只插线段树划分出的区间里。查询的时候就是定位到的都查一下(标记永久化)。然后还要维护子树堆顶 max.

删除操作的话就直接把走到的节点需要删的就删掉然后结束递归。当询问区间并非完全包含当前节点代表区间时,还需要把误删的加回来。然后发现“把误删的加回来”最多只有两次,也就是左右两端的时候。所以每次只会增加 O(1) 个段。

现在加入和删除的段数是 O(nlogn) 的了,然后考虑向下递归的时候必然是要加入一个段或者删除一个段,所以向下递归的复杂度也是 O(nlogn),那么总复杂度就是 O(nlog2n)

P4786 D

1,1,使得前缀和都 0,后缀和都 0.暴力怎么做?前搞出前缀和,然后把如果前缀 min 更新为负数了就贪心改掉,然后再倒过来改。需要把这个过程写成式子的形式:先花费 min{prek},然后考虑 i 后缀删的个数就 (min{prek})(minj<i{prej})sufi=sufi+(min{prek})(minj<i{prej}),然后再花费 min{sufi}

那么总的花费就是:min{prek}min{sufi}=min{prek}min{sufi+(min{prek})(minj<i{prej})}

=minj<i{sufi+prej}

线段树维护分治信息。

LOJ6507 B

猜到是某种势能均摊了,但是没想明白这个均摊为啥对的。

上棵线段树,记录这个区间有哪些位是已经被 区间或/与 确定好具体值的,那么再对这个区间整体覆盖这一位的时候就能得知答案了,否则继续往下递归。

考虑将每个节点的势能设置为不完全一致的位数。势能每次最多只会增加 O(log),然后花费的时间复杂度就是释放了多少势能。

P5069 D

这个题目名很美。

拉了,没看出来。注意到如果操作一个位置,那么它两边的位置就一定不会被操作了,所以就可以将答案加上这个数然后删掉它和它旁边的数。那么考虑每一个峰,答案就是这些峰的值以及中间的奇数和或者偶数和。树状数组维护一下,每次单点修改的时候讨论一下搞出答案的变化量就行。

这位更是重量级的做法是线段树维护分治信息。依然是考虑一个点选了之后它两边就一定不会被选所以可以删掉。然后考虑线段树去合并分治信息,那么就需要记录一下 ans0/1,0/1 表示有没有带左端点,有没有带右端点,的答案是什么。还要记 l0/1,0/1,r0/1,0/1 表示这种情况下左/右端点有没有选。

这个的好处是可以支持区间查询,区间加。

P5070 D

r 作扫描线,加入一个 x 的时候把 [x11,x+11] 里所有值最后一次出现的位置给提出来,从右往左扫,处理出答案的变化,那么就变成区间加单点查。

P5576 D

先考虑 n 个串的最长公共子串有啥做法,然后进行一下魔改。

首先就是把它们都查到广义 SAM 里面,然后看子树中有所有串 endpos 的节点的 len 的最大值。

现在是区间询问,那么就是包含颜色 [l,r] 的节点的 len 的最大值。在 parent 树上线段树合并好像不太行,那就 set 维护连续颜色段然后启发式合并。保证 len 从大到小合并,每次合并出一个 [l,r] 之后,考虑它成为那些点的答案,对于每个询问 [L,R]R 挂到 L 上,通过线段树记录区间最小值就能找出能更新的询问,更新完之后再在线段树里把这个询问删掉。这样复杂度就是 polylog 的。

ICPC 2022 Jinan L B

https://codeforces.com/gym/104076/problem/L

看上去很经典,想一想发现不是很会。那就考虑一下特殊情况,发现链的情况就是区间一维最近点对(CF1793F / CF765F / P5926),那还是得从那个题搬做法来。

作扫描线然后每次找前面第一个比之前答案还有小的,最多找 log 轮那个思路不太能套,树上怎么扫描线¿

搬分治的做法,点分治,然后找支配对。支配对数是 O(nlogn)

以后遇到序列上分治做的题目,就想想能不能搬到树上点分治搞,这样出题还挺好/cf

lxl 说是 codechef 某题 D

多组询问 minlx<yr{axxoray}. 

支配对的套路。找出数量级比较小的点对使得答案一定会在它们当中取到。

枚举一个 i,考虑它作为后面那个 ay,有用的 aj 有哪些。枚举点对 (aj,ai) 的 LCA 的深度是 dep.考虑如果有两个位置 j,k 都合法,那么 LCA(aj,ak) 深度一定更深,则 (j,k) 会更优。所以只有编号最大的那个 j 是有用的(当然大前提是 j<i)。

换而言之,若此处有 k<j<i:如果 [l,r] 只包含 j,i,那么就只有挑选出的支配对 (j,i) 有用,如果 [l,r] 包含了 k,j,i,在前面一定会有更优的。故而这样只跳出 O(nlogV) 个支配对,然后问题就变成了 2-side min 问题了。

CF1446D2 D

全是重量级!O(nn) 拉了,来看看 1log 和线性的做法。

遇到最优化题目或者什么的,先想想全局最优解是什么,能否用它来简化问题(P8511 TEST_68)在这里就发现全局众数一定是答案区间的众数之一。如果一个区间的众数不是全局众数的话,考虑左端点一步步往左扩,右端点一步步往右扩,在最后全局众数会超过之前的众数,那么一定存在某个时刻它们两个相等,从而得到了更长的合法区间。

也就是如果一个区间众数不是全局众数,那么一定存在一个更大的答案,假设众数是 x,枚举另一个众数 y,将 x 出现位置设成 1y 出现位置设成 1,求个最长的和为 0 的区间。

思路是考虑如果暴力做是 O(x+y) 的,如果 x>>y 就寄了。但是考虑此时有很多 x 是一定不会用到的。具体而言,如果从这个 x 开始往后跑,前缀和都 >0,并且往前跑也都是 >0,那么这个 x 一定用不到。想想怎么找出有用的 1

先看往后跑,前缀和都 >0.用折线图思考这个东西,发现每次 1 会让它前面一个 +1 合法.具体而言,对于每个 1 匹配上它前面最近的还没有被匹配的 1.那么只有匹配成功的 1 是有用的。

反过来也一样,可以做同样的事情求个交,当然也可以接着上面那个继续跑。用 set 暴力实现是 O(nlogn) 的。

咋优化到线性?求交和最后统计都挺简单,问题就是对于每个 y,每次和它前面一个没被匹配的 x 匹配,求出所有被匹配的 x.由于 x 是固定的,预处理出每个 y 前面第一个 x 和后面第一个 x

只看 x 的话,相当于初始全 1 的序列,每次查询某个位置前面第一个 1,然后将 1 置成 0.但是 y 后面第一个 x 一定没有被删掉,所以其实是查询一个 1 前面第一个 1,那就用链表维护一下就可以了。找完之后记录一下链表咋改变的再回退即可。

复杂度被优化到了线性。

P6105 B or D?

先求个 maxi,jS,i+j<C{i+j},考虑一下桶,下标之和 <C,且 C 是个常数,套路就是反转其中一个,那就令 a 为桶,b 为桶反转之后的结果,再适当右移一位,则相当于询问 maxai=bj=1,i<j{i+Cj}动态开点线段树 卡空间 只能 平衡树维护分治信息。我谔谔,不知道能不能过,懒得写。

或者考虑一下会贡献给答案的最优匹配实际上是双向的,如果要 insert x,找一下 y 的最优匹配 z,如果没有这个 z 或者 z<x,那么 x,y 就是一个双向的最优匹配,x+y insert 到堆里面。然后可能会破坏 y 原先的最优匹配,看一下 z 的最优匹配是否是 y,如果是的话再把 z+y 给删掉。

erase 也同理。把 insert 倒过来做就行。

CF453E A

没看明白题解区都是些啥。。。混乱。

先上个 set 维护颜色段,初始的那个特殊处理一下。现在问题变成了 O(n+m) 次询问初始全 0,区间 [l,r] 在时间 k 时候的和,差分成 [1,r] 的减去 [1,l1] 的。

扫描线扫序列维,在时间维上每次就是一个等差数列加和一个区间加。然后询问一个单点的和。对差分数组就是一个区间加 前缀和问题,上线段树就行。时间复杂度 O((n+m)logm)

/yiw B

从云浅那里看到的题:区间排序,区间求和。

先上个 set 维护连续段。然后每个连续段开棵值域线段树,线段树分裂 + 线段树合并,这样就能维护出每个连续段的总和。然后现在区间求和就是查询若干个连续段的和,还有一个连续段的后缀,一个连续段的前缀。

在 set 里定位一下连续段,后两个直接在值域线段树里查一下。前面那个连续段的区间和可以不写平衡树,一个连续段的值记在左端点上,用线段树维护之即可。时间复杂度 O(nlogn)(忘了线段树分裂复杂度了,那就 n,m 统一成 n 算了。)

QOJ5098 D

明明应该是简单题的 ... 为什么想不出来!!

考虑一下不带修怎么做,原来是我的不带修做法做麻烦了。

考虑令 Li 为右端点为 i 时最靠左的左端点,首先一定有 L 不降。那么询问 [l,r] 时,右端点 i 就分为 Li<lLii 两种,二分一下分界线,前面的是 sumisuml1 可以直接算,后面是 sumisumL[i]1 那就是区间最值。

现在带修了,先考虑咋维护 L.对于一个 (prej,j),相当于将 jLi 均和 prej+1 取个 max,那每次单点修改的时候就是扔掉若干个限制再加进来若干个限制,不能删除,但考虑一下只有插入还是很好做的,那就上一发线段树分治,用线段树维护 L,那后缀 cmax 也是线段树上二分一下会把哪段区间整体覆盖掉(L 不降),那么 sumisumL[i]1 的区间最大值也是很好维护的了。

时间复杂度 O((n+q)log2n)

P6864 B

怎么场上只有四个过的??

先考虑没有撤销操作,就是每次外套一对括号或者后加一对括号,考虑合法括号子串数 s 怎么变:

  • 外套:ss+1
  • 后加:令 ct 为整个串能被划分成多少个 (A) 这样的串拼接起来,然后 ss+ct+1

现在又需要维护一个 ct,考虑 ct 怎么变:

  • 外套:ct1
  • 后加:ctct+1

发现转移是线性的,那就写成矩阵的形式就可以了。线段树维护矩阵连乘积,这样也能支持撤销操作(也就是删除一个矩阵)。

posted @   do_while_true  阅读(84)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 46 seconds

点击右上角即可分享
微信分享提示