其他分享
首页 > 其他分享> > 竞赛日记 1.14-3.26

竞赛日记 1.14-3.26

作者:互联网

2020 竞赛日记

目的是总结没有来得及写完的题目中蕴含的idea以及梳理每日所学。

Jan.

14
DP杂题 \(I\)

​ 题目本质上是求四维偏序。先描述一个矩阵,处理成一个仅带有0和1的四维矩阵,再告诉你一个四元组 \(<ai,bi,ci,di>\),求解\(i,j,k,l(i<=k,j<=l)\)满足

\[\begin{cases} \ i \ge \ ai \\ \ j \ge \ bi \\ \ k \le \ ci \\ \ l \le \ di \\ \ f[i][j][k][l] = 1 \end{cases} \]

的总方案数。考虑到类似这样的二元组\(<ai,bi>\)描述的是一个无限扩展的平面,把每个\(f(i,j)=1\)的点投影到整个平面中,如此,便可\(O(1)\)求出某个点右上方所含的包含\(1\)的点。至于投影,如果需要 \(ai<=i\)则在\(x\)轴上作后缀和,反之作前缀和[1]。这个东西的名字叫偏序,二维的叫做二维偏序,四维则叫做四维偏序。

  组合数本身的性质适合递推,考虑到每个物品只有选与不选两种,设计方程\(f(i,j)=f(i-1,j)+f(i-1,j-1)\)。矩阵加速并滚掉\(i\)维度即可。

  可将题目转化为选取任意个带权集合,各个集合没有交集,使得最终的权值最大。

  大致是利用二进制,对于每一个\(a\)对前面的\(a\)做一遍背包,再将所有背包根据一点奇技淫巧合并即可。合并蕴含了贪心的思想。

15

“\(AC\)自动机这个东西不难,现学现用嘛。”

“二项式反演很简单的,知道\(f(i)\)推\(h(i)\)很简便的。”

​ —— \(dalao\) 安博施

“机房就是一个摩尔庄园。”

​ —— \(Imakf\)

DP杂题 \(II\)
16

“期望的本质是积分。” —— \(Imakf\)

“信息学最重要的是数学基础。” —— \(ysuperman\)

“期望可以代表整体。如果这串数随机选一个期望为\(k\),你就认为每次选择都是\(k\)。”
—— \(lightmain\)

概率期望 概念
概率期望 例题 [2][3]
概率DP
如何设计有强度的调试数据
17

  “人生就像动态规划,你的一个又一个阶段是由上天安排的,而你,决定的是在这一阶段可以由上一阶段的哪些状态转移而来。越勤奋,越幸运,并不代表这一次你决策的方向有多么优秀,却代表着现在的这一个状态能够续写多少可能的结果。”

好像与竞赛内容无关,算辽

18 小年快乐

“\(Oh,my\ sweet \ summer\ child,what\ do\ you\ know\ about\ fear\ ?\)”

"生于夏季的小可爱啊,你又知道什么是危险吗\(?\)"

​ ——《权力的游戏》

​ 首先应区分好选择某点与覆盖某点的不同。看题解前本题给我的一个困扰是如何处理兄弟节点相互覆盖的问题。题解是在父亲节点处处理兄弟节点互相覆盖的问题。而实现这个转变只需要考虑每个节点覆盖到向上\(x\)层的最优方案就可以了。

​ 本题的难点在于我们不能确定一棵子树究竟是走到底还是走回来。而这个问题的关键又在于最后是否回到子树的根,而非经过子树的根几次。一定要把握住问题的关键去设方程。

19
20

​ 题目的难点在于如何实现平方和的递推。考虑到每一次求解平方和时:\(ans(now)=\sum_ {i=0}^{up}ans(i)\) ,而\(ans(i)=i^2*\sum_{i'=0}^{up'} num(i')+\sum_{i'=0}^{up'} ans(i')+2*i*\sum_{i'=0}^{up'}sum(i')\)(其实也很好理解,就是完全平方式的展开公式,由于是累加所以带个\(\sum\))所以每次记录下\(num(i)\)符合条件的数字个数、\(sum(i)\)符合条件的数字和以及\(ans(i)\)符合条件的数字的平方和即可实现递推。

​ 二分+数位DP。

21

​ 一道小技巧很多,非常优秀的题。首先考虑一维情况下,所有变换后有金子的格子上的数字\(x\)一定可以写成\(2^a * 3^b * 5^c * 7^d\)的形式。于是将n以内的这样的数离散化,对每个数作一遍数位DP,看看它能由多少数字转移过来(记为\(c(i)\))即可。二维的答案就是\(c(x)*c(y)\)。处理出前\(k\)大也需要技巧,可以利用大根推来维护。

​ 两道斜率优化模板题。斜率优化注重式子的变形,在变形过程中可以自己定义大量参数来简化过程,但在打代码时一定要注意自己定义的参数在\(0\)处的初始化。

23

​ 关系并查集。区间\([l,r]\)的奇偶性不好转移,记前缀和为\(S(i)\),则区间\([l,r]\)的奇偶性实际上就是\(s[l-1]\)与\(s[r]\)的关系。据此建立关系并查集即可。

25 大年初一

"\(POJ\)的题目都好难。"

​ ——罗艺翔

​ 思维题。重点是要发现当倒叙遍历时,\(Ai+1\)就是当前位置上的数字在剩余的数字中的排名。值域线段树维护就可以啦。

​ 扫描线的思路挺好想的,重点是如何维护线段树。可以这样考虑:每个区间有一个标记,如果标记\(>0\),则整个区间都被覆盖,反之,则该区间被覆盖的部分由左边被覆盖的和右边被覆盖的组成。据此维护线段树即可。还应注意这个方法没加标记下传,是有局限性的,不可随意推广。

26
27
28
平衡树 \(Treap\)

​ 值域线段树好题。首先观察题目发现是插队问题,于是从链表或逆推两个角度去思考。但是由于题目每次询问给的是排名而非值,而排名一直在变化,所以用链表不好做。首先可以发现,当前数\(Ai\)的含义是轮到\(i\)时\(i\)之前有多少人,那么逆推推到\(i\)时,\(i\)之后的已经各就其位了,\(i\)就应该站在第\(Ai\)个空位置上。查询第\(Ai\)个空位置,选择用值域线段树。完结撒花~

​ 求和线段树好题。我首先所想的也是考虑要求前后缀的最大子段,但我没有把它深入去思考,上升到维护的层面。还是思维太局限了 这题给我的最大启示就是同一棵线段树绝不仅仅只能维护一个\(sum\)啊,\(max\)啊,它还可以维护许多东西,只要区间整体的信息是由子区间的信息传递来的,就都可以维护。

​ 关系并查集好题。先总结一下关系并查集。关系并查集常用于只知道两者之间关系,每个数隶属于哪个集合不确定的题目。常见的题目为各种循环有序集等。每个集合中的元素关系相互确定,且值均以代表元素的值为\(0\)进行计算。不同集合中的元素关系无法确定,但可以通过转化进行合并。本题为关系并查集的变式。枚举法官即可。

29

​ 关系并查集+动态规划。加深了对关系并查集合并时的一点理解:可以看成是数轴上的四个点\((x,y,fx,fy)\),其中\(dis(x,y)=d,dis(x,fx)=val(x),dis(y,fy)=val(y)\),自然\(,dis(fx,fy)=val(x)+d-val(y)\)。

30

​ 线段树的灵活运用。注意处理时保留的是最长的连续值的长度,至于求解第一个位置,可以递归时处理。毋需保留。

​ 求解平面上\(n\)个矩形的周长交集。显然扫描线+线段树。细节注意:由于求解长度时必然是两个坐标相减,若对于一个\([1,4]\)的区间,其结果由\(x[2]-x[1]+x[3]-x[4]\)转移而来的话显然少了\([2,3]\)这一段,所以每次求解答案时应\(x[r+1]-x[l]\)。查询的一开始时候把右端点\(-1\)就可以啦。

31

区间静态第K大,线段树上持久化。

​ ——沃茨基 · 硕德

被逼的无奈只能拿可持久化开刀了QAQ

主席树
主席树经典应用

​ 可持久化的基础。模板题。

​ 静态区间第\(k\)大问题模板。首先,现在考虑权值大小问题时学会往平衡树和权值线段树方面去想。其次,平衡树上每个点保留的是区间中的数本身,不具有可减性,而权值线段树上每个点保留的是当前区间内小于等于区间右端点的数的总数,具有可减性。于是考虑可持久化权值线段树解决本题。

​ 个人感觉可持久化的题目中最难的一道。首先要明确的是,由于可持久化中只支持单点修改,所以不能够进行路径压缩。其次,在寻找集合的代表节点中,由于我们已经把之前的信息都继承过来了,所以\(f\)数组中一直都是当前节点的父亲点值,而非某一个之后添加的编号。换句话说,\(f\)数组里的值恒小于等于\(n\)。顺便学了带秩合并。

​ 本质上与之前的很相似。结合题目的区间要求,可以给\(Trie\)树加个\(cnt\),\(cnt\)具有可减性,与之前的可持久化权值线段树就一致了。

Feb.

2

​ 本来应该是个哈希题的,但我用哈希死活\(TLE\),后来学习网上的用\(short\)过的。 \(excuse\ me\)?!!

​ 与上题一样是个哈希模板,但我用\(vector\)的哈希死活\(TLE\),网上的链表做法可以过,我\(copy\)下来自己造数据跑结果跑的没有我的程序一半快\(qnq\)。心如死灰地抄题解走人。

​ 三个题都揭示了\(KMP\)算法中\(Fail\)数组的其他性质。\(Fail\)数组的本质是前后缀匹配的最大值。先摆结论:假设匹配\(t\)数组,若\(n\%(n-Fail[n])=0\),那么\(t[fail[n]-1,n]\)必然为串\(t\)的最大周期子串。证明很简单,由于\(n\%(n-Fail[n])=0\),所以原串必然可以被分成\(k\)份,每份\(n-Fail[n]\)个。由\(Fail\)数组的定义可知,将最大前缀与最大后缀拎出来比较可以发现第一份必然全等于第二份,第二份必然全等于第三份......以此类推即可得证。同时,按照这个方法对\(Fail[i]\)进行一次,可得到次大周期子串。

扩展\(KMP\)
3
\(Manacher\)算法

​ 本题的实质是求解两个数组\(L\)和\(R\),满足\(L[i]\)为以\(i\)为左端点的最大回文子串长度,\(R[i]\)为以\(i\)为右端点的最大回文子串长度。我最开始的想法是边\(Manacher\)边更新所有的\(L[i]\)和\(R[i]\),但不幸地\(WA\)掉了,原因诸如这组数据:\(BBABBA\),\(L[5]\)(从1标号)显然是\(5\),然而由于计算\(p[4]\)时\(R\)端点更新为了\(6\),故\(L[5]\)计算错误。之后我就发现:每个\(L[i]\)与\(R[i]\)一定都是由某个\(mid\)推出来的\((i\in[Mid-p[Mid],Mid+p[Mid]])\),再通过递推对目前所有满足条件的\(Mid\)带来的值取\(max\)即可。

\(AC\)自动机
后缀数组\(SA\)
4

  但依旧打了2h才出一个题目QAQ,实现能力太差了...

树状数组初步

​ 好题。我一开始的想法是主席树,但发现很难使节点具有可减性。正解是离线算法,将所有的待求解区间排序再处理。在线转离线+排序是个很棒的思路。

​ 较灵活的线段树。启示:线段树上某个区间的值被修改后若会影响到其兄弟节点的值,不要放弃思路,考虑是否能在兄弟节点上继续递归维护,将复杂度做到\(O(log^2n)\)。

​ 标记永久化的精髓体现。永久化的标记一定是基于整个区间得来的。即当前节点的标记是能覆盖到节点所代表的整个区间的。同时,标记永久化后每个节点的答案就由递归路径上的所有节点决定,而不是它本身的值单一控制了。标记永久化灵活简便,但貌似不可以以此处理所有的标记。使用时要注意。

​ 经典好题。当要维护两个懒标记(如同时有区间乘法维护\(mul\)和区间加法维护\(add\))时,需自定义一个求答案的方法。重点:先考虑如何求答案,在考虑标记之间如何互相维护。本题中,求解答案既可以一路向下求每一层的\(add×mul\),再加入答案之中;也可以只是求\(\sum add\),最后加上当前节点值。再考虑如何维护。若用第一种方法,则每次新增一个\(add\)时,加入标记的必须是\(add/mul\),\(mul\)则自由维护;而用第二种方法,每次新增一个\(add\)时,加入标记的是\(add×mul\),\(mul\)仍然自由维护。显然,第二种维护避免了实数运算,高下立判。

5

​ 线段树经典应用:区间覆盖问题。第一次独立尝试标记永久化写题,但仅对值作了永久化处理,覆盖标记没有任何传递处理。(注:覆盖标记是不能永久化处理的!)这个题目给我一个血淋淋的教训:任何标记,要么永久化处理,要么就下传,总之一定要被传递。

​ 区间覆盖问题。思维难度不大,独立思考做出来的。但代码量嘛...\(4kb\)警告...,而且基本上包含了很多基础内容。打完以后觉得自己的线段树实现能力更强了。对了,这个题\(1A\)。

​ 区间覆盖问题的变式。题目一个很重要的性质:当第一次对序列排序后,任意时刻序列均有序。对于“削去\(>=\)大于等于\(Limit\)部分”的操作,考虑分为三步实现,首先,二分查找(与性质联系上了!)出第一个大于等于\(Limit\)的点,然后,区间求和\([limit,n]\),最后,区间覆盖\([limit,n]\)。区间求和与区间覆盖可以合并写。性质找到,思路理清后,代码实现就很简单了。注意:由于存在区间求和操作,则每次临时求解区间最大值操作复杂度为\(O(logn)\)。直接维护区间最大值来简化代码和时间复杂度。

​ \(Siano\)的强化版,再维护一个区间最小值即可。技巧:设定一个\(f\)函数,\(f(x)=k1*x+k2*...+kn\),每次传入不同的\(k\)值,来实现同一函数维护各式各样的乘法加法。\(1A\)

6

​ 以往都是用倍增去做,但今天学到了\(RMQ\)做法和\(tarjan(Orz)\)两种方法。尤其是\(tarjan\)做法,在离线情况下可以做到近乎线性的复杂度。 可是我太菜了我写的tarjan比以前写的倍增还慢

​ 图论题目有许多技巧,比如本题需要用到结论:树上任意点集的极小连通分量的边权和=按时间戳排序并首位相接后的\(\sum dis(a[i],a[i+1])\),\(dis(i,j)\)表示树上两点的最短路径长度。令\(d(i)\)表示节点i到根节点的路径长度,那么\(dis(i,j)=d(i)+d(j)-2*d(LCA(i,j))\),问题转化为在线求解\(LCA\),大功告成。

​ 思路:用\(Kruskal\)求最小生成树时是根据边权来贪心的,所以有:最小生成树上的两点路径上的边权最大值小于等于连接两点的任意一条非树边。依据这个性质,暴力枚举每条非树边,倍增求出该边上的两个端点树上路径的最大和次大值来更新答案即可。

​ 类似于模板题。

​ 枚举情况,拆分区间分类讨论。

​ 线段树好题。题目要求区间最大子段和,且相同的数只算一次。注意到条件:相同的数只算一次,联想到HH的项链那题,在线转离线+询问区间排序,每次询问区间右端点右移时都类似于区间加法。本题亦然。将询问的区间根据右端点由小到大排序逐一处理。令\(last[i]\)表示数字\(i\)出现的上一个位置。维护序列\(s[i]=\sum_{j=i}^{k} a[j]\)(\(k\)为当前外层循环中的原序列中的某个数)(计算\(s[i]\)时相同的数也只算一次),那么一定在某个时候\(s[i]\)求出了区间\([i,j]\)(\(j>i\))的最大子段和。但我们不能只看当前的最大值,因为以前出现过的某个值可能更大,且也是合法的(就相当于某一段没加上来),所以我们维护一个历史最大值。同时,我们还应该维护一个历史最大\(tag\),表示从上次\(pushdown\)到现在的最大的延迟标记(也是相当于如果有一段不够优秀,我们就不加它了)。综上,共维护四个量\(Tag,Max,hMax,hTag\)借以维护\(s[i]\)序列的当前答案就可以了。

7
平衡树\(Splay\)

​ 首先输入方式就暗示我用平衡树写(当然好像权值线段树也可)。然后就是一个简单的线段树优化\(DP\)。

​ 区间翻转模板题。当平衡树维护一个序列时,中序遍历即为原本序列,类似查询第\(K\)大就成了查询序列第\(K\)位。\(Splay\)还有一个很好用的性质,就是当要对区间\([l,r]\)操作时,可以将\(l-1\)转到根节点,\(r+1\)转到根节点的幼儿子,这样\([l,r]\)就全都在\(r+1\)的左子树上了,方便操作。区间翻转就是\(r+1\)左子树上的每个节点的左右儿子都互相交换。这题给我的最大启示:平衡树既可维护数列(节点之间有大小关系),也可以维护一个序列(节点之间有对应在原序列中的先后关系),两种维护说白了最后计算答案(排序后的数列或者原序列)都是用中序遍历。而只要是中序遍历,旋转操作就不会改变该树的性质。甚至除被旋转的两点外,其他点的性质都可以不变。(如本题中待旋转点的爷爷旋转时甚至可以不下传标记)。

8

\(——My\ queen,one\ not\ does\ simply\ struggle\ against\ the\ fate.\)

\(——I\ dare\ change\ this\ fate.We\ will\ fight\ valiantly\ to\ the\ last\ Enderman.\)

​ \(——\)"\(Ender\ Wish\)"

​ 题目不难。基本是Splay基本操作组合一下。TLE了一次,本以为Splay写挂了,后来发现是由于数组开小了\(QAQ\)(存在删除节点再添加节点所以要开两倍空间或建一个\(rubbish\)数组)。但平衡树本身还是没有写挂!开心!越练越熟练!

点分治

​ 点分治+树状数组。或者点分治+双指针也可以。

​ 以后要练习点分治用这题,不要用模板。我最开始的点分治求重心假了,但是过上两题轻轻松松......感谢这一道题把我卡飞了,让我意识到了自己找重心的问题。(特别鸣谢:\(yxt\ (Orz)\)指出我找重心的错误并教会了我真的求重心方法)。

​ 点分治+二维偏序。这一题可以说是前几题的综合版。既限制权值最大值,也限制边数最大值。前几题都可以简单的每次找重心求解,但本题只能用容斥方法写点分治了。点分治的容斥,应该算最初级的那种。先\(Calc\)一遍\(p\)值,由于我们其实只需要经过\(p\)点的,那么所有起点终点在同一棵子树里的答案需要减掉。枚举每条边调用一遍\(Calc\),注意此时应加上之前的必经的路径长度和边数。

​ \(DFS\)序的灵活运用。考虑到难点是处理\(B\)点在\(A\)的子树中的情况,也就是求\(\sum_{i\in subtree A}size[i]\)。\(DFS\)序有一个很好的性质,就是节点\(u\)子树中的每个节点的\(dfn\)值\(\in [dfn[u],dfn[u]+size[u]-1]\)。这东西显然可以用线段树维护。而由于限制深度差不能超过\(k\),所以我们以深度为下标,而只知道深度并不能确定该点是否在子树上,所以我们需对每个点建立一棵完整的线段树——显然,套上可持久化就可以咯。

9

​ 感觉不错,写完平衡树后也没怎么调平衡树。但输入数据太坑了\(qnq\),硬是拖了我很久。

树链剖分

​ 子树内的修改查询/两点间简单路径上的修改查询模板题。

​ \(Exciting\)! 可能是蕴含的思维难度不大吧 我看了一会就想到了区间覆盖线段树维护,\(2h\)打完调完交一手直接过了哈哈哈。太开心了。以后做题都要介个样子\(QAQ\),良性循环。

感觉点分治和树链剖分都是模板居多??

10

​ 这道题关键在于如何处理添加和删除操作。区间加法自然是可以的,但有一种更好的办法。当有添加操作,并且每次操作的对象都是整个数列时,我们可以记一个变量\(delta\),加减操作都直接在\(delta\)上进行,那么每次的答案就是\(v+delta\)。注意一点就是把数字插入树中时也要减去当前的\(delta\)。剩余的就是模板了。本题也让我对\(splay\)中的\(Find\)操作有了更深的理解,当存在原数时,\(Find\)返回原数对应的树上结点值;当不存在时,返回原数的前驱或后驱。由于不存在原数,所以大于前驱的所有数大于原数,小于后驱的所有数小于原数。

话说我这个题只用了1.5h就A了 看来韬哥说得对哈 真是越打越熟练

\(LCT\)

如下图是森林中的一棵树,图一为原树,图二为加\(splay\)维护后的树。绿框内的树处于同一\(splay\)中。

  1. \(Access(x)\):将节点\(x\)所在的树的根到节点\(x\)的链变为实链,且起点为根节点,终点为节点\(x\)。实现方法:先将\(splay(x)\)使该点成为所在\(splay\)的根,由于节点x为终点,所以此时直接\(c[x][1]=0\)实现断边,此时\(f[x]\)指向了上一个\(splay\)的某个节点,\(splay\)那个点,再让它的右儿子直接等于此时的\(x\)(认父不认子),既实现了断边有实现了添边。令\(x\)等于当前节点,再往上反复到根节点即可。
  2. \(Makert(x)\):将节点\(x\)变为所在的树的树根。实现方法:先\(Access(x)\),再\(splay(x)\),此时由于\(x\)深度最大,我们将整个\(splay\)翻转后,\(x\)深度最小,就是原树的根了。为什么可以直接翻转呢?由于其余的\(splay\)维护的是里面各点的相对深度关系,所以其余的\(splay\)都是不变的。譬如图二,我们交换\(A\ E\)两点后,\(G\)所在的\(splay\)显然是不变的。
  3. \(Findrt(x)\):找到节点\(x\)所在的树的树根。常用于判断连通性。实现方法:先\(Access(x)\),再\(splay(x)\),一路向左找就可以了。建议此时再\(splay(y)\)以保证复杂度。
  4. \(Split(x,y)\):拉出一条节点\(x\)到节点\(y\)的链。该链上包含且仅包含节点\(x\)到节点\(y\)上的简单路径上的点。实现方法:先\(Makert(x)\),再\(Assert(y)\)。好显然啊

    标签:二分,1.14,题目,题意,线段,日记,3.26,可以,节点
    来源: https://www.cnblogs.com/parauni-blog/p/12586872.html