复杂数据结构

1|0复杂数据结构

一些巨大的数据结构题目

1|1CF1336F Journey

题意:给定一棵树和 m 条链,求多少对链的交中包含的边 k

思路:首先对链交的情况进行分类。

第一种是 lca(x1,y1)lca(x2,y2),我们在深度较大的 lca 处统计答案,那么我们把一条链的贡献记在端点向 lcak 步的位置,将其子树加,统计答案时求两端的答案即可。

第二种是 lca(x1,y1)=lca(x2,y2)dfn[x1]<dfn[x2]<dfn[y1]<dfn[y2],那么就枚举 lca(x1,x2)=z,从 zy2k 步,再统计答案,那么具体做法就是对于所有 lca=x(u,v),建出 u 的虚树,把 v 挂在 u 上,同时维护 u 所在链,维护答案需要用线段树合并,维护链需要启发式合并。

第三种是 lca(x1,y1)=lca(x2,y2)dfn[x1]<dfn[y1]<dfn[x2]<dfn[y2],那么和第一种类似,它们的交一定是从 lca 往下 k 个,在 y1 处子树加,在 x2 向上走 k 步统计答案即可。

1|2P5385 [Cnoi2019] 须臾幻境

题意:求一段区间内的边形成的连通块数。

牛逼题。对于求联通块数量,有一点小trick。

首先,如果在一棵树上断掉一些边,那么连通块是就是点数减边数。而转到图上,可以任取一颗生成树森林,此时的点数减去在生成树森林上的边数就是连通块数。

回到这道题,我们需要的是求出一段时间内存在于生成树森林上的边数,我们可以先顺序加边,用LCT实时维护生成树森林,即如果当前要加入的边的两端已经联通,就删掉路径上最早加入的边,用主席树存下每一条边存在的时间就可以了。

1|3P4848 崂山白花蛇草水

题意:在线带插入矩形第 k 大。

思路:在线带插入矩形第k大,看起来就不可做,结果可以直接暴力,搞两个数组然后插入到小的数组里,如果大于n就归并到大数组里,询问时顺次查就可以了。

1|4P5356 [Ynoi2017] 由乃打扑克

题意:区间加,区间 kth。

思路:算分块维护kth的经典trick:维护每一块排好序后的数组,查询时在每一块上二分。可以做到 O(nnlogn)

1|5P7963 [NOIP2021] 棋局

思路:终于来写棋局了。

其实思路不难,就对于每类边分别维护一下。

对于第一类边,可以直接维护;

对于第二类边,可以用并查集,然后维护一下段头、段尾。

对于第三类边,用线段树合并即可。

关于去重,第一类点的去重比较容易,第二类点的去重稍微有点麻烦,不过因为这一类点在横或纵坐边上是连续的,因此在线段树上是一个区间,这样也好处理。

然后就是吃子的问题。对于第一类边,比较容易。对于第二类边,会被吃的最多只有 4 个,可以直接判。对于第三类边,我们在维护线段树的同时维护一下与当前连通块相邻的棋子集合,然后查询就是一个前缀查询。

1|6P8868 [NOIP2022] 比赛

题意:给出两个序列 a,b,每个区间的贡献是 a 序列中的最小值乘上 b 序列中的最大值,多次询问一个区间的所有子区间的权值和。

思路:以前以为是什么单调栈 + 线段树的神仙题,结果发现就是个用线段树暴力维护半群信息的题目。

先把询问离线下来,然后对 r 进行扫描线,维护当前所有 l 的答案。我们对 A,B 同时维护单调栈,这样对于两边都是一个区间覆盖,然后我们维护 Sl,r=r=lMaxAl,r×MaxBl,r,这样的话查询就是区间和。

考虑怎么动态维护这个信息。我们发现标记其实就是区间覆盖和区间 +=X×Y 的复合,那么我们可以维护 sx,sy 表示 x,y 的区间覆盖标记, ax,ay,ax,y,a 分别表示区间和会增加多少,这样就差不多了。

1|7P9247 [集训队互测 2018] 完美的队列

题意:有 n 个队列,每个队列有长度 ai,操作是往区间的每个队列中加入一个权值,求每个时刻队列中的权值种数。

思路:分块好题。

首先,可以转化成对于每一次 push 的操作,求出其最晚的被 pop 的时间。然后我们对序列分块。对于整块,我们用 two\_pointers 求出第 i 次操作后最早的时刻 j 满足 (i,j] 中的操作可以让每个位置插入次数超过 a。对于散块,可以扫描线 + 树状数组上二分。块长取 nlogn 时最优,复杂度是 mnlogn

1|8P8360 [SNOI2022] 军队

题意:有两个数组 a,b,有给 a 区间赋值,给区间中所有 ai=x 的位置在 b 序列中加 y,和查询 b 区间的区间和。

思路:考虑分块。

对于整块,可以考虑维护森林。初始颜色相同的点有相同的父节点,然后区间赋值操作就是合并这些颜色。

合并时,如果这个颜色块内没有,就需要新建一个点代表这个颜色,然后再合并。

对于区间加,在树上打标记即可。

对于散块,可以暴力下放标记,然后暴力维护操作和重构森林。

复杂度 O(n+qn)

1|9「JOISC 2016 Day 3」回转寿司

题意:给出一个有 N 个点的环,环上各点有一个初始权值 ai

给出 Q 个询问,每次询问给出一个区间 [l,r] 和一个值 A ,对于 A 的变动定义如下(r 可能会小于 l 因为是环形):

for (int i = l; i <= r; i++) if(a[i] > A) swap(a[i],A);

对于每个询问,回答遍历完区间 [l,r] 后 A 的最终值。

思路:先考虑 l=1,r=n 的部分分。假如我们只进行一次操作,那么我们最后得到的 A 一定是 A 和序列最大值中较大的那个,如果 A 比序列的最大值要小那么 A 会加入序列中,最大值会出来。如果进行多次操作,我们发现在这个情况下我们并不需要知道序列最终是什么样,我们只用知道这个序列中元素组成的可重集,就可以求出每次操作后 A 会是什么,于是我们用堆来维护即可。

然后考虑拓展到一般的情况。我们此时的目的肯定是想办法尽量只关注序列的可重集,而题目的数据范围不大,时限却很大,不难想到分块。

我们将序列分块,每一块维护元素的可重集和每次会将对整个块进行操作的 A 有哪些。对于每次操作,整块可以直接把 A 加入可重集,然后把最大值弹出,这就是操作完的 A。

重点是散块如何处理。我们顺次考虑每个数,然后顺次考虑 A,如果当前的 ai>A,这样 ai 会变成 A,A 会变成 ai,然后会继续找到下一个比 ai 小的 A,继续进行下去。不难发现,ai 最终会变成 A 中最小值和 ai 中较小的那个,如果 ai 比 A 中最小的数还大就会和这个数交换。

设块长为 B,那么我们一次修改整块的复杂度是 O(nBlogn),散块的复杂度是 O(Blogn),于是总复杂度就是 O(Qnlogn+nlogn)

1|10P6109 [Ynoi2009] rprmq1

题意:有一个 n×n 的矩阵 a,初始全是 0,有 m 次修改操作和 q 次查询操作,先进行所有修改操作,然后进行所有查询操作。

一次修改操作会给出 l1,l2,r1,r2,x,代表把所有满足 l1ir1l2jr2ai,j 元素加上一个值 x

一次查询操作会给出 l1,l2,r1,r2,代表查询所有满足 l1ir1l2jr2ai,j 元素的最大值。

思路:因为是先加再询问,可以想到离线处理。那么可以想到枚举查询的左端点,然后扫描线,把加操作差分一下,那么就变成了求区间历史最值,这样做是 O((n2+q)logn)

我们来分析一下这样做的实质。我们做的是加入 [1,l) 的操作,不维护历史最值,然后加入 [l,r] 的操作,维护历史最值,于是可以考虑用分治优化。

这里我们用猫树分治,因为这样只用把一个区间拆成 2 个而不是 log 个。处理区间 [l,r] 时,我们先加入 [l,mid] 的操作,然后打清空历史最值的标记,然后处理 [r,mid] 的操作和询问,处理完后回退到打标记的状态,递归处理右子树,然后再处理 [l,mid] 的操作和询问,最后递归左子树。

复杂度 O(nlog2n+qlogn)


__EOF__

本文作者Xttttr
本文链接https://www.cnblogs.com/Xttttr/p/18015349.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Xttttr  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示