Codeforces - tag::data structures 大合集 [占坑 25 / 0x3f3f3f3f]

371D

小盘子不断嵌套与大盘子,最后与地面相连,往里面灌水,溢出部分会往下面流,求每次操作时当前的盘子的容量

其实这道题是期末考前就做好了的..

链式结构考虑并查集,然后没了(求大佬解释第一个T的点)

https://paste.ubuntu.com/p/tFycq2zYqz/


242E

线段树操作,1.求\([l,r]\)的和,2.更新\(a[l,r]\)\(a[l,r]⊕x\)

对于操作2,把线段树拆位后就变为01翻转操作了

https://paste.ubuntu.com/p/9J73wp4V7V/


1000C

求被1...n次线段覆盖的各端点个数

口胡一下,离散化乱搞


858D

给出n个定长10的字符串,求每一个字符串的最短可区分子串

把所有的后缀挂到字典树上,然后n次从小到大暴力枚举子串,若字典树中删去相应的单个子串后不存在或等于自身剩余的相同子串个数,则表明该子串为可识别最短子串

另外解法的细节上的不同是不统计自身相同子串,而是维护前缀串来自于哪个原串,似乎做法这种更优

//此处应有代码


808D(水)

给定数列\(a[1...n]\),最多可移动一个元素到任意地方,问是否可以把数列分割成连续的两块

如果某一块比另一块的总和大\(t\),那就看大的那块有没有\(t/2\)大小的可移动到另一边,哈希表O(n)完成

https://paste.ubuntu.com/p/BZCKYHvJgq/


629D

LIS那啥,我讨厌这题,不做了


342E

给定一棵n节点的树,除了1节点为X外其他全为O

有m个操作,操作1为置某节点为X,操作2为询问某节点与最近的X节点的距离

qls曾说过是树剖套路题,题解给了一种很好用的分块暴力方法

对询问分块处理,把每\(\sqrt m\)个置X节点存入块中

每当块满了就进行一次\(O(n)\)的bfs更新所有节点的最近距离,然后清空,复杂度\(O(n \sqrt m)\)

否则先取先前一直做好的bfs的最近距离,然后与块内的置X节点进行暴力LCA询问取最小距离,复杂度\(O(m \sqrt m)\)

这里忽略询问节点部分的分块会使效率高不少

https://paste.ubuntu.com/p/znTK2GzGD3/


670E

给出括号匹配串,求m次操作后(移动和删除,注意删除时是匹配的一连串删除)的匹配串

用栈预处理出所有左右括号匹配的下标,删除时用并查集相连两端表示一连串删除,移动时检查当前下标并查集是否为空或有删除标记,有就大幅度滑动,O(n)精神AC


519E

多次询问一棵树中到达某两点u,v距离相等的点的个数

这次做的太纠结了,情况比较麻烦

考虑u,v路径的长度,若为奇数,则不存在路径中点,输出0,
否则就是中点分支上所有点的个数,除去到达u方向的分支和v方向的分支,此时用size来表示就是中点的size减去深度较深的点距中点低一层的祖先的size,即使单链也是如此
需要注意的是若中点为LCA要单独考虑(注意size域的方向),答案为所有的点减去uv两边距lca低一层的size

https://paste.ubuntu.com/p/8wMcF8ykwk/


474F

数论不行,只想到了GCD但没有联系到区间的最小值

https://blog.csdn.net/S_Black/article/details/47189051

实现比较简单就不搞了


914D

给定\(a[1...n]\),两种操作,操作2单点更新,操作1询问区间gcd能否等于x,询问过程中可允许暂时修改最多1个数

分析一下,如果某个数不为x的倍数,那么它与任何数gcd都不可能为x,必须要改
如果某个数为x的倍数,那么它可能不改,而由另外一部分限制它因子指数的上限也可以做到gcd为x

因此,在线段树中查询区间gcd且统计底层不为x倍数的个数

若个数为0,则证明gcd恰好为x的倍数,虽然不能直接表明相等,但任意修改其中一个数为x那么gcd就为x
若个数为1,我们只能在不为x的倍数的值上修改
其他情况均为非法

https://paste.ubuntu.com/p/MP829x39Yf/


685B

求所有子树的重心

性质:把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
递归处理
一个点的树的重心是他自己。
如果一棵树的根的所有子树大小都不超过整个树大小的一半,那么树根是重心。
否则找到子树大小超过整个树大小的一半的子树(只会有一个),假定当前树的重心为子树的重心,用调整的方式得到当前树的重心。


280B

\(a[1...n]\)的所有区间的最大值与次大值的最大异或值

这种一般都用到单调栈,只是写的过程极为混乱ORZ

只要定扩展区间的中心为次大值那就写起来很轻松了,只需向前向后分别各扩展一次

我怎么就想到定最大值找次大值从左边枚举右边RMQ+二分BLABLA找了呢..

https://paste.ubuntu.com/p/BgHq2bhbkt/


731F

给定\(a[1...n]\),允许选定一个数字,使小于它的数为0,大于它的数变为不超过自身大小的它的倍数,然后累和,求该操作下累和的最大值

用哈希表存储并统计前缀个数和,类似素数筛地枚举倍数,分段累和

或者排序然后每次lowerbound查找

https://paste.ubuntu.com/p/h8KkDNshwG/


903D

变种前缀和,求数列\(a[1...n]\)所有区间的差,若两数相差小于等于1则不计算

算好前缀后再补回不计算的部分就行了,小的减掉,大的加回来

令人恶心的是会爆ll,因此用long double

https://paste.ubuntu.com/p/WGX8brYW74/


367B(水)

给定数列\(a[1...n]\)\(b[1...m]\)\(p\),找出符合每项相差p的a子数列使与b匹配

枚举所有等差长度的数列O(n),是否相等直接用map对比(还挺好用的)

(这题居然能放div1)

https://paste.ubuntu.com/p/xWszDDTgRn/


292E

给定数组\(a[1...n]\)\(b[1...n]\),
操作1使\(a[L...R]\)快速复制到\(b[k...k+R-L]\),保证操作不越界
操作2查询\(b[k]\)的值

感觉蛮有创意的一道题,我一开始想到用并查集维护区间信息了(按道理应该可行吧)

另一种思路是用线段树区间覆盖,值仅与询问前最后一次覆盖到它的更新油管
\(b\)建成线段树,只需知道当前节点的开端和复制\(a\)的开端以及自身节点所在的对应数组下标就能推出\(b\)目前的任一点信息
注意没有更新的特判

具体看代码

https://paste.ubuntu.com/p/kpNS9cPXn5/


766D

推断同义词和反义词

挑战程序设计竞赛同款题目,略


920F

给定数组\(a[1...n]\),操作1使\(a[L...R]\)变为各自数值对应的因子数\(D[a[i]]\),操作2求和

由于因子数能在\(O(logn)\)时间内降至平凡个数1或者2,直接树上暴力\(O(nlognlogn)\)

https://paste.ubuntu.com/p/4fMC7HKdfd/


961E

给定\(a[1...n]\),求有多少对\((i,j)\)满足\(i<j \ \& \ a[i]≥j \ \& \ a[j]≥i\)

没有第三个条件那是相当好办,多了那条件就变得极其地绕(我是这么觉得)

首先,要方便地求大小关系和数量关系,树状数组是必须的,注意到大于n的都没用那么把每个元素比较一下取min

求解的方法可以这样枚举:\(i\)是一个一个插入的,那么对于\(j,j>i\)而言,求有多少个\(a[1...i_{max,max≤i}]≥j\)的元素

所以我们要维护一个对于每一个j能遍历到的最远的i,以满足\(i≤a[j]\),但因为枚举过程是i开始的,那么倒过来维护\(vec[maxto:i]={j_1,j_2...}\)

https://paste.ubuntu.com/p/Kr2gV27DHC/


375D

给出一棵有颜色的树,多次询问某子树下相同颜色节点大于某个值的颜色个数

第一反应:树分块啊
第二反应:不大会写hhh

由于是子树问题,那么可以通过启发式合并离线处理或者dfs序+线段树暴力(不大靠谱)解决

先挂着


920E / 190E

给你一个\(n\)个点\(m\)条边的无向图,求其补图的连通块个数及各个连通块大小

暴力遍历


707D

主席树模板/可持久化KD树(当我没说)


Gym - 101142G

题意:给你一棵树,边为水流过的路径,叶子为房子其余为闸开关,有一些抢匪会占领屋子或者撤离屋子,你需要求出每一步最少关闭的水流路径,其次要求最少误杀的房屋

正解是直接用DFS序来求,但我尝试了自己的二分线段树方法

首先是要解决的是如何表示水流路径/开关/屋子与误杀的问题
对原树求出叶子的标记,开了一棵用于标记是否为叶子的线段树ori,那么误杀的房屋=ori线段树上的区间求和-占领线段树1的区间求和(由于没用到快速复原的操作所以线段树2等价于ori)
我们还多开一棵用于标记总开关的线段树0,若某节点为1则表示该节点上方的流水被截断

观察容易得出每一步操作最多只影响到自身除1以外的最远祖先,这时候RMQ就派上用场了,但需要改动1以下的儿子的父亲为自己,这样会方便二分

为什么要二分,很显然占领数从叶子到顶上是一个单调递增的过程,我们只需找到一个占领最多的lowerbound就可以断定某一点要断水流

注意求之前要吧最大子树的影响清零,如最小误伤人数要先减除先前误杀的人数,然后再重新统计误伤人数

大概思路是这样,实现过程中注意到了如果删除某节点后只有另一边有一个占领点,那询问的节点的原路径会错误地二分到它的父亲,这时要特殊标记一下直接断定最佳截断点

目前ans1是正解,不知道ans2错在哪里了..明天醒来再看吧

https://paste.ubuntu.com/p/4nccbsYyrC/

UPD:我不改了,用正解方法秒A

https://paste.ubuntu.com/p/KvkhyYNqkx/


558E

给你一个仅26字母的串,q次操作,k=1时使[L,R]升序,k=0时降序,求最终的串

使用计数排序和26个线段树即可


609E

给出一个图,多次询问求强制包含(u,v)边的最小生成树的权重

先求出MST,若强制包含的边为MST里的边那就不作处理,否则必然形成一个包含u,v的环,此时的最小生成树不同在于必须要删除环中的最大权边(环外显然不受影响)

求的方法常见的对链操作有树链剖分,另一种较为巧妙的是倍增法,一边求祖先一边更新MST路径的最大权边值

//此处应有代码


689D

已知区间\(a[1...n]\)\(b[1...n]\),求多少个区间满足\(max(a[L...R])=min(b[L...R])\)

显然区间有单调性,枚举左端点,二分右端点,两次二分找出最近和最远的右端点,O(1)或O(logn)计算贡献即可


587C

一棵树中的节点带有权值集合(多个权值),多次询问求路径\((u,v)\)的第\(k\)小(\(k<10\),每次均可不同)的权值是多少

k很小,那么每个节点维护一个大小为10的大根堆,树剖暴力合并,常数应该不爆炸吧?(似乎st表维护堆也行)

明天上代码


475D


877F

待做(不想做/做不动):644B/359D/755D/830B/487B/777E/724D/514D/637D/721D/534D/754D/893D


834D(unordered)

题意:给出\(a[1...n]\),问划分成k个连续序列的最大代价,其中每一个序列的代价为序列中不同的数字种类

列出朴素的DP方程\(f[i][j]\):把前\(i\)个数字划分成\(j\)短的最大代价,转移\(f[i][j]=max(f[k][j-1]+s[k+1][i])\),其中\(s[i][j]\)表示\(i\)\(j\)的个数种类

有一种动态维护种类的方法是记录每个数上一次出现的位置\(last[a[i]]\),每次枚举到\(i\)时更新\(s[last_{a_i}+1,i]\)加一,那么\(s[j]\)就代表当前\(j\)\(i\)的种类个数(感觉好神奇

基于这种策略改写方程:\(f[j][i]=max(f[j-1][k]+s[k+1])\)

第二维可用滚动的线段树来维护,s直接错位累加到上一维的\(f[j-1]\)中(直接加一就好)

https://paste.ubuntu.com/p/vJVNK6kMPN/


913D(unordered)

题意:给出n个任务和总时间T,每个任务占时间c_i和要求最后完成的不得超过t_i个任务(包括)才能得1分,求最高得分和方案

倒序枚举得分k,当t_i≥k时才是有效得分,用平衡树动态维护前k大的和,具体看代码

https://paste.ubuntu.com/p/Z3dztvY5Jv/


HDU待做 : 5111/6010/5700

posted @ 2018-07-03 19:23  Caturra  阅读(287)  评论(0编辑  收藏  举报