Everyday DS
Day 1 CF1270H Number of Components#
发现极大的连通块形如 [l,r][l,r] 区间形式,其中满足 l−1mink=1ak>rmaxk=lak,而且 rmink=lak>nmaxk=r+1ak。
所以区间 [l,r] 的右端点满足 rmink=1ak>nmaxk=r+1ak。将 ≥ar 标 1,<ar 标 0,那么序列形如 111⋯100⋯0 的形式(前面一共有 r 个 1)。这是充要条件。
由于数列的数互不相同,所以可以尝试枚举 ar=v,合法的 r 个数即满足序列形如 111⋯100⋯0 的 v 的个数,即满足 01 序列中只有一对相邻形如 10 的串的序列的个数,这对相邻的 10 对应唯一的 (ar,ar+1)。
所以对于一对 (ai,ai+1),满足其在 01 序列中形成形如 10 串的 v 满足 v∈[min(ai,ai+1),max(ai,ai+1))。所以我们给区间 [min(ai,ai+1),max(ai,ai+1)) 上的数全部 +1,表示这对 (ai,ai+1) 会给这段区间的 v 的 01 序列贡献一个 10。
最后我们需要求出 10 个数为 1 的序列的个数。考虑到离散化后对于所有的 v,其 01 序列中至少都会有一对 10,直接维护最小值以及最小值个数即可。
Day 2 Ynoi2015 我回来了#
介绍个最劣解 O(m√n+n√n+nα(n)lnn) 做法。
首先令 bi←ai−1,区间 [l,r] 的答案就是:
考虑如何动态维护后面那个式子。我们对每一个 k∈[1,n] 维护一个大小 n/k 的 bitset,代表所有 bi/k 的值,最低位的 0 就是 k 对应的 mex。
加入一个 bi 的时候考虑所有 bi/k 的值,不难发现只有 O(√n) 种取值,于是可以分成 O(√n) 个区间修改,每次修改形如 (l,r,x),代表往 l 到 r 每个 bitset 中插入数 x。
因为 n 个 bitset 的总大小不超过 nlnn(调和级数),所以如果我们每次修改 (l,r,x) 都能找到一个是 0 的位,把这位换成 1,并不访问别的 1 的话,这个复杂度就是正确的。于是不难想到对每个 x 维护一个并查集,这样就可以从一个位置 p∈[l,r] 迅速跳到下一个第 x 位为 0 的 p′ 了。
然后我们在跳 p 的时候需要动态维护 p 的 bitset 的 mex,由于只有插入没有删除,这显然可以均摊 O(nlnn) 维护。得到这个 p 的权值后,就只需要一个单点修改 O(1) 的数据结构来维护 m 次区间和查询了。显然分块可以做到 O(1)−O(√n)。于是就得到了 O(m√n+n√n+nα(n)logn) 的优秀做法。
Day 3 BalticOI 2020 小丑#
整体二分。
有个小技巧,就是可以把存边的数组往后复制一遍,然后删去区间 [l,r] 就相当于保留区间 [r+1,l+m−1] 的边。于是只需要解决这么个问题:
给定一张 n 个点 m 条边的无向图,q 次询问,每次只保留区间 [l,r] 的边,问是否是二分图。
乍一看有点像 Cnoi2019 须臾幻境?但是其实有不用 LCT 的做法。
考虑令 fl 表示最小的 p 使得 [l,p] 区间不是二分图。显然 f 具有单调性,即 ∀i≤i′,fi≤fi′。只需要求出所有的 fi,就可以直接根据 fl 是否 ≤r 判断是否是二分图了。
由于 f 的单调性,不难想到整体二分。令 S(l,r,vl,vr) 表示处理 fl,fl+1,⋯,fr,并且 vl≤fl≤fr≤vr。令 mid=l+r2,于是只需要求出 fmid 即可:用可撤销并查集从 mid 开始加边,第一个使得图不是二分图的位置就是 fmid。
为了保证复杂度,需要在分治之前将 (r,vl) 的边先加进并查集中。然后就没什么细节了。
复杂度 O(mlogmlogn+q),整体二分复杂度写假的是小丑。
Day 4 SDOI2016 游戏#
令 lca(u,v)=w,su 为 u 到根的距离(随便指定一个根),考虑 u→w→v 的路径修改:
- x∈{u→w}:nx←a(su−sx)+b=−a⋅sx+(b+a⋅su)。
- x∈{w→v}:nx←a(su+sx−2sw)+b=a⋅sx+(a(su−2sw)+b)。
考虑树链剖分,将修改拆分到 O(logn) 条重链中。由于重链中 id 连续,这是关于 id(u) 的一次函数形式。李超线段树维护区间线段 min,在区间中插入线段即可。
李超树区间插入(非全局)是 O(log2n) 的(分成 O(logn) 个区间后分别再在每个区间中插入),所以复杂度是 O(nlog3n) 的。不知道为啥能过。
Day 5 CERC2014 Pork barrel#
考虑 Kruskal 算法的过程,即边权从小到大依次加入边,每条边连接两个连通块。很重要的性质是每次加入的边边权单调不降。
于是权值 [L,R] 内的边组成的最小生成树,相当于从权值为 L 的边开始跑 Kruskal,取所有 ≥L 的边组成的最小生成树,树上所有边权 ≤R 的边的边权之和。
将边从小到大排序,如果对于每个边权 L,求出边权 ≥L 的一段后缀的边组成的最小生成树,就能够通过一些可持久化手段维护出此时边权 ≤R 的边的边权之和。
若已经求出 L′≥L 的最小生成树(L′ 是 L 值域上的后继)T′,考虑新增 (u,v,L) 这条边,得到新的最小生成树 T:
- 若 u,v 在 T′ 中联通,找到 u→v 简单路径上边权最大的边,删去这条边,然后加入 (u,v,L)。
- 若 u,v 在 T′ 中不连通,直接加入 (u,v,L)。
注意到 n≤103,这允许我们在 O(nm) 的时间内求出这 m 棵最小生成树。
由于 T′→T 的边权变化是 O(1) 的,于是只需要一个支持单点修改,区间求和的可持久化数据结构。使用可持久化权值线段树即可。
复杂度 O(nm+(m+q)logW)。
Day 6 CF1801G A task for substrings#
好神奇的题啊,我完全不会做。
建出 s1,s2,⋯,sn 的 ACAM。
考虑在 r 处统计满足条件的数对 (l,r) 的贡献。那么需要求出 fr 表示文本串以 r 为结尾的前缀 [1,r] 中,其所有后缀中模式串的出现次数。即:
li 表示第 i 个文本串即 si 的大小。
考虑询问 [a,b],b∑i=afi 表示的是所有 r 在 [a,b] 之间的串的个数,缺少对 l 的限制。实际上,我们要求 l∈[a,b]。由于现满足 r≤b 且 l≤r,只需要满足 l≥a 即可。
哪些 r 的贡献是正确的呢?若满足 r 为后缀的最长匹配到的文本串的 l 要 ≥a,那么 fr 就正确地贡献到了答案里面。形式化地,记 gr 为文本串前缀 [1,r] 的后缀中匹配的最长模式串编号,即:
显然地,f,g 都能够通过 ACAM 处理出,如果找到最大的 p 满足 p−|sgp|+1<a,r∈[p+1,b] 的贡献就能直接通过 f 的前缀和求出。
对于 r∈[a,p] 的贡献呢?画图理解,实际上只需要求出 sgp[a:p] 这个子串里面文本串的出现次数即可。考虑如何对每个串 si,预处理出其长度为 j 的后缀中包含的文本串 ci,j,直接对文本串的反串建 ACAM 即可。
如何找到最大的 p 呢?考虑二分。如果使用 O(1) RMQ,预处理 O(|t|log|t|) 的话可能会很慢,除非你用四毛子,但是众所周知四毛子常数巨大。可以线段树上二分,复杂度 O(|t|)−O(log|t|)。
于是我们就在 O(|t|+mlog|t|+∑|si|) 的复杂度内被这题解决了。
Dqy 7 CF1446F Line Distance#
计几结论拍脸,感觉不如原神。
Binary search is your friend.
考虑二分答案,二分一个距离 r,考虑求出 d(O,AB)>r 的无序点对 (A,B) 数量。
以 r 为半径作圆 C:x2+y2=r2。考虑如果一个点 A 在 C 的边界或内部,那么任意的 d(O,AB) 都会 ≤R。于是先排除 x2A+y2A≤r2 的情况。
当 A,B 在圆的外部时,d(O,AB)>r 当且仅当直线 AB 与圆 C 无交。
令 A 关于圆 C 的切点弦对应的劣弧为 A1A2(为什么没有弧的 LATEX 记号?因为这里不支持 amsmath 宏包……)。难以发现,直线 AB 与圆 C 无交,当且仅当 A1A2 与 B1B2 有交,证明是简单的。
难以发现,A1A2 与 B1B2 有交当且仅当 A2A1(A1A2 翻转)与 B1B2 有交。于是从某个点开始将圆 C 断开,每个弧 A1A2 都可以找到一段弧度区间与其对应(如果区间被这个点断开可以取翻转后的区间)。
然后就变成了给若干个形如 [li,ri] 的区间,求有交的区间对的个数。二维数点即可。复杂度 O(nlognlog(wϵ)),ϵ=10−6。
D8y a CF1718F Burenka, an Array and Queries#
显然先考虑把每个 ai 只因数分解,令 S(x) 表示 x 只因子的集合。
令 Sl,r=S(r∏i=lai)=S(al)∪S(al+1)∪⋯∪S(ar)。假如我们快速求出了 Sl,r:
考虑容斥,钦定 T 为 Sl,r 的子集,T 中的数都为 i 的只因子。具体地,设 fS 表示 S 集合恰为 i 的只因子集合的方案数,gS 表示 S 集合为 i 的只因子集合的子集的方案数(也就是钦定 S 中的数为只因子,其余任意),S⊆Sl,r:
我们要求 f∅:
对 T 中的只因子 i∈Sl,r 根号分治:
- i≤√C,直接记忆化枚举 2π(√c) 种情况即可,不难发现满足 ∏i∈Ti≤C 的 T 不多,加点减枝卡一卡就行了。
- i>√C,不难发现一个 T 中至多含有一个这样的 i,也就是说剩下的全是 i≤√C 的只因子。于是只需要单独处理一个 i>√C 的所有倍数的贡献。即 −∑i>√C⌊Ci⌋。但是这样做还有个小问题。在 i≤√C 的时候我们已经计算过了 i>√C,i∈T 的贡献,于是我们应该钦定 ⌊Ci⌋ 个数中只能计算 ∄ i∈Sl,r,i≤√C,i∤j 的 j 的贡献。
注意到 i>√C 时 ⌊Ci⌋≤√C,直接莫队维护所有的 i>√C,然后开个桶每次询问 O(√C) 暴力跑即可。
复杂度 O(q(√n+√C)+2π(√C)),但是后面那个根本卡不满。
Da9 y JOISC 2022 复兴计划#
不难发现题目要求对于每次询问,第 i 条边边权为 |wi−x|,求出最小生成树。
这个绝对值性质多多,比如呈现斜率为 1 的 V 字形。
这说明对于两条边 i,j 图像,必然至多只有一个交点。所以对于每条边 i,其有影响 x 是一段区间 [li,ri],i 被算进询问为 x 的生成树中,当且仅当 x∈[li,ri]。
同时不难发现 li,ri∈{wi|1≤i≤n}。于是可以将边权从小到大排序,维护出一段后缀边集的最小生成树。具体地,考虑如何由 i≥l+1 的 i 组成的最小生成树 Tl+1 推出 i≥l 的 i 组成的最小生成树 Tl:
- 设权值第 i 小的边连接 u,v。
- 由于 n≤500,可以 O(n) 找出 Tl+1 中 u→v 的路径 P,或者 u,v 不连通。
- 若 u,v 不联通,直接加入 i 边,即 Tl←Tl+1∪i。
- 若 u,v 联通,由于 i 边权的最小性,删去 P 中权值最大的边 j,然后加入 i,即 Tl←(Tl+1∖j)∪i。同时发现 x<wi+wj2 时必定取 i 更优,x>wi+wj2 时必定取 j 更优。所以更新 lj=ri=wi+wj2。
上述求出 li,ri 的过程可以做到 O(nm)/O(nmlogn)。
于是对于询问 x:
这东西随便用个数据结构差分维护一下就行了。复杂度 O(nmlogn+mlogw)。
标记永久化可以大大降低代码常数。
D1y a0 CF1129D Isolation#
考虑 dp,令 fi 为 [1,i] 这个前缀的分段方案数。i 从小到大扫描线,动态维护 cj 表示 [j+1,i] 中只出现恰好一次的数的个数:
考虑如何维护 cj,扫描线过程中维护 li 表示 ai 上次出现的位置。那么 i−1→i 相当于给 [lli,li) 区间 −1,[li,i) 区间 +1。
所以问题转化为:
- 单点修改 fi。
- 区间修改 ci。
- 区间(前缀)查询 ci≤k 的 fi 总和。
分块即可。复杂度 O(n√n),可以做到线性空间但没必要。
不知道有没有 O(n poly(logn)) 做法。
1ay 1D CERC2014 Mountainous landscape#
这是一个跑不过双 log 的单 log 做法。
考虑双 log 做法是怎么做的。令 ai(1≤i≤n) 为给定的 x 坐标递增的点序列,开一棵线段树维护区间上凸壳,第 i 次查询相当于在 [i+2,n] 区间组成的点的上凸壳中,找到一个在经过 ai,ai+1 两点的直线上的点 aj,那么线段 aj−1,aj 就是答案。
这个过程显然可以 O(nlogn) 预处理之后,线段树上二分实现。复杂度 O(nlog2n),因为每次查询都要二分斜率所以是双 log 的。
有一个避免二分的做法。考虑对 i=1,2,⋯,n−2 的询问直线 ai,ai+1 离线下来并按照斜率从大到小排序。那么每次在线段树节点中查询的斜率是单调递增的,于是记录一个 px 表示当前斜率下查询 x 的切点位置即可。
由于线段树节点凸壳点数是 O(nlogn) 的,所以 px 的总移动次数是 O(nlogn) 的,所以复杂度是 O(nlogn) 的。但是实测跑得没有二分快,可能是因为 px 的移动次数卡得比较满。
1Da 2y CF418E Tricky Password#
不难发现发现 a2=a4=a6=⋯,a3=a5=a7=⋯,于是只需要维护前 3 行的值即可。
不难发现 a2,x 为 a1,x 在前缀中出现的次数,a3,x 为 a1,x 前缀中至少出现了 x 次的数的个数。
不难发现分块即可。
不难发现分块只需要维护 O(√n) 个块内 O(n) 个值,分别表示对于每种权值,在块的前缀中的出现次数。查询时继承 y 所在的块的前一个块的答案,然后剩下的一段后缀暴力跑即可。
Da 1y3 CF1017H The Films#
今天因为初赛实在是没时间(懒得)写题了www,就放一道之前模拟赛场切的题吧。还有这个 CF 评分是假的,难点在于看懂题。
考虑令 ci 表示序列中 i 元素的出现次数,对于一次询问 l,r,令 di 表示 al,al+1,⋯,ar 中 i 元素的出现次数。令 Amn=n!(n−m)!=nm_ 为从 n 个数中选出 m 个组成排列的方案数,那么 [l,r] 询问的答案为:
我们发现对于相同的 k,排列数底下的数是相同的,并且括号右边乘上的排列数显然只和 r−l+1 及询问区间长度有关。
注意到 k 不超过 100 种,这启示我们将询问按照 k 分类。对于 k 一定的询问,右边的部分可以 O(n) 预处理出来。并且对于任意的 i∈[1,m],di←di+1 或者 di←di−1 时,括号内的答案是好计算的。所以可以直接跑莫队,取两部分求乘积即可。
我们分析一下复杂度,设 t 为 k 的个数,qi(1≤i≤t) 表示第 i 种 k 的询问的个数,则 t∑i=1qi=q。对于第 i 个 k 的询问我们取块长 n√qi ,这部分的复杂度为 O(n√qi),则我们关心 t∑i=1√qi 的最大值,而根据卡尔松不等式:
则 t∑i=1√qi≤√t(t∑i=1qi)=√tq,所以复杂度是 O(n√tq) 的,但是常数不大所以直接冲过去了。
1Dan 4 CERC2018 The Bridge on the River Kawaii#
你说得对,可以用整体二分做到 O(nlognlogV),但是 V≤10。
于是直接线段树分治,维护 V 个并查集,第 i 个维护把边权 ≤i 的边加入后的图。
查询时找到最大的 i 使得 X,Y 联通即可。复杂度 O(Vnlog2n)。
D1a5y ARC165F Make Adjacent#
记录 x(1≤x≤n) 出现位置分别为 lx,rx(lx<rx),讨论一下发现当两个数 x,y 满足 lx<ly,rx<ry 时操作后 x 一定出现在 y 前面,不然可以交换位置以达到更优步数。否则发现无论怎么操作发现都不影响答案。
所以我们将 x 描述为平面上的点 (lx,rx),操作为依次在平面上选择一个点删去,且需要满足删去的点左下角没有还没被删的点。直接建图,将 (lx,rx) 向所有 (ly,ry),lx<ly,rx<ry 连边,跑字典序最小拓扑序就是最优解。
但是这样边数是 O(n2) 的,注意到点数 O(n),考虑优化建图。
按照 lx 从大到小扫描线,每次相当于对于一个 ri 排序后的后缀连边。这样的连边是一段区间,可以线段树优化建图,线段树建立在 r 序列上。
但是 lx 向左移动时会插入新的 rx,为了让上一时刻的连边不包含新插入的 rx,需要可持久化。
复杂度 O(nlogn)。
1D6y a APIO2017 斑斓之地#
回忆平面图欧拉公式。
V 为点数,E 为边数,F 为面数,C 为连通块数。
以下称河流为黑块,土地为白块。将白块看成点,四联通的白块之间连边,不难发现矩阵查询即询问导出子图的连通块数。考虑平面图欧拉公式,那么只需要求出导出子图的点数、面数以及边数即可。
点数是好求的,只需要查询矩阵内有多少白块即可。
边数也是好求的,考虑白块上下连边或者左右连边两种情况,容斥一下,只需要考虑相邻两块中存在黑块的情况。把上下相邻两个块的贡献标在上面的块,把左右相邻的两个块的贡献标在左边的块,就不会算重了。
同样,面数也是好求的。4 个白块组成 2×2 的子矩形时才会多产生一个面。同样容斥,把 2×2 矩形中黑块的贡献放在左上角,还有一种情况就是矩形区域包含了黑块路径,这个特判以下即可。
然后答案就好求了,变成二维数点,主席树即可。复杂度 O(nlogn)。
Dq17 y CF1599E Two Arrays#
考虑斐波那契通项公式,分别维护区间 (1+√52)a1,i+a2,i 和 (1−√52)a1,i+a2,i 的和。显然可以扩域,定义一个 n=a+√5b 的结构体即可。然后快速求斐波那契数列某项就可以直接快速幂了。
然后就是线段树心跳板子。很难写,不想写。
然而有个看起来复杂度很假的做法。
考虑简化 segment tree beats,直接维护区间 max,区间 min。考虑区间对 c 取 max 操作,若区间 min≥c,则修改对区间没有影响,可以直接 return
掉;否则递归下去,但是要等到找到连续段才进行修改。区间取 min 同理。而对于区间加,直接维护加法标记即可。
显然区间加法、区间查询的复杂度是正确的,考虑区间取 min 或者 max。
记 B 为序列总连续段数,假设这次修改影响了 c 个连续段,那么 B′←B′−c+k,k 为新增的连续段数。并且这次修改的复杂度应该是 O(clogn) 的(可以认为是修改是把区间要被修改的连续段,每段分成 logn 个线段树区间)。
问题是 k 可能很大,感性理解一下,发现 k 其实就是区间中没有被影响到的连续段数 +1。所以 ∑c 不一定与 n 同阶。
这东西是可以且很容易被卡满的(比如这样),所以我们就成功证明出这个做法的复杂度,其实就是是假的。
但是直接过了,还怪好写的嘞。所以正确做法以后再补吧。
1D8 ya CF671D Roads in Yusland#
设 fu,i 表示覆盖了 u 子树并且向上覆盖到了深度为 i 的最小代价。
考虑合并儿子 v:
相当于区间加,单点取 min,区间求最小值。
直接线段树合并维护就没了,可能要写垃圾回收。复杂度 O(nlogn)。
D19 ay CF573D Bear and Cavalry#
用排列维护出 wi 排序后第 i 个人对应的 hi 排列后的第 j 只马,记为 bi=j。
根据排序不等式,排序后尽量使第 i 个人匹配第 i 只马是不劣的。
类似这道题的匹配方式,可以猜想 i 只能和 j(j∈[i−2,i+2]) 匹配。事实上这是对的,同样可以通过调整法证明。
于是令 fi 表示考虑前 i 个人匹配的方案中最大的权值。每次转移分讨一下,相当于新增 1/2/3 对匹配,分别从 fi−1,fi−2,fi−3 处转移。
关于交换操作,bi 容易(其实不那么容易,因为你要维护一堆映射关系)维护,那么相当于单点修改 bi。设 ci,0/1/2 表示 i−0/1/2 到 i 构成 1/2/3 对匹配的最大权值,于是 fi←fi−j−1+ci,j(0≤j≤2)。修改 bi 同时单点维护 ci,0/1/2。
转移式是线性的,而且每次单点修改,使用矩阵动态 dp 维护。复杂度 O(k3qlogn),k=3。
2D0y a Luogu5891 Fracture Ray#
结论拍脸。
显然如果 i→i+popcount(i) 这样连边的话,连出来是一个森林。
结论就是 q 个 u 到根的路径的点,去重后的个数不超过 8×107。
然后 bitset
维护所有走过的点,建出虚树,点数就变成 O(q) 的了。
然后树剖线段树就能在 O(qlog2q) 的时间内解决。
要手写 bitset
。
Day 21 HNOI/AHOI2018 转盘#
容易发现最优解里一定存在一种方案,为「一开始停留一段时间,然后一直往下一个取」的形式。通过调整容易证明。
断环成链,直接列出式子:
令 ti=ai−i(1≤i≤2n),然后让 i 平移 n−1 位,化简后得:
相当于对每个 i,求出其后缀最大值 aj。反过来,考虑每个 j,其作为 i 的后缀最大值最小的 i。那么要求的就是 j 前面第一个满足 ai>aj 的 i+1,可以单调栈解决。
现在有了修改,直接线段树维护单调栈(楼房重建线段树)即可。
复杂度 O((n+m)log2n)。
Day 0001 0110 ZJOI2019 语言#
考虑对每个点 u 计算贡献,求出所有经过它的路径的两个端点,包含这些点的最小连通块大小就是以 u 为端点的 (u,v) 答案数对的个数。
根据经典结论,对于 m 个点的点集 u1,u2,⋯,um,钦定 u0=um 且根据 DFS 序排序,则以任意点为根,包含它的最小连通块大小为 m∑i=1dui−dlca(ui,ui−1)。du 为 u 的深度。
发现如果知道所有经过 u 的路径的端点,再插入一条经过 u 的路径,u 的贡献也是容易通过在 DFS 序上建立线段树动态维护的。于是相当于 m 次链修改,每次修改在 u,v 之间的路径上的线段树插入点 u 和 v。
树上差分一下,变成单点修改。然后父亲节点的线段树需要从子节点继承而来,线段树合并即可。
使用 DFS 序求 LCA,复杂度 O(nlogn)。
Day P9 湖北省选模拟2023 山路长环 / ring#
如果有权值为 0 的边,用所有这样的边把环分成若干条链。
不难发现若起始点在链的一端,先手必胜当且仅当链长(边数)为奇数。可以进行归纳证明,这种情况下先手每次移动必将边权置为 0。
继续推性质:
- 起始点在长度为奇数的链(奇链)上,那么删掉这个点后,这条链将会分成两个链,总恰好有其中一个链的长度为奇数,所以先手往那条链走并将走过的边权置为 0 即可。先手必胜。
- 起始点在长度为偶数的链(偶链)上,那么删掉这个点后,剩下两条链长度要不同时是奇数要不同时是偶数。根据前面的结论,奇数时先手必胜,偶数时后手必胜。
- 起始点在长度为奇环上,先手随便走一步并将边权置为 0,后手操作时相当于偶链的情况,于是先手必胜。
- 起始点在长度为偶环上,若先手直接将一条边权值置为 0,后手操作相当于奇链的情况,此时后手必胜。所以先手尽量不会将边权置为 0,不难得到两者策略均为走同一个方向,每次将边权 −1。由于是偶环,必定存在一个时刻,使得所有边权都减去了原来的边权最小值,即被分成若干的链,而棋子停留在起始点。所以先手必胜当且仅当此时状态下先手必胜。
所以线段树维护区间最小值、区间长度、区间最左侧的最小值到区间左端点的长度、区间最右侧最小值到区间右端点的长度、区间被最小值分割后每个段中(不包括两边,下同)奇数长度的段的长度 +1 总和、区间内被最小值分割后每个段中偶数长度的段的长度总和除以 2 即可。复杂度 O(qlogm)。
Day 4! ARC063F Snuke's Coloring 2#
首先容易找到周长为 2(w+1) 和 2(h+1) 的矩形,所以答案下界是 2(max(w,h)+1)。
考虑按照整个矩形中心坐标,将矩形分成 4 个子矩形,观察到若有矩形完全包含于其中一个子矩形,则其周长必不超过 2max(w,h),必然不是最优解。所以最优解一定被直线 2x=w 或者 2y=h 穿过。
先考虑被直线 2x=w 穿过的情况,后者可以通过将 (x,y) 两维坐标交换转换为前者。
设将 x 坐标离散化后第 i 个坐标为 ti,第 i 个点坐标为 (xi,yi)。为了方便,加入 (0,0) 点和 (w,h) 点,先计算 ti 到 ti+1 形成周长 2(ti+1−ti+h) 的矩形,然后剩下的矩形中必定至少跨过三个坐标 tl,tj,tr。
考虑扫描线,从左到右枚举答案矩形的右边界为 tr,设左边界为 tl,那么只有 l<j<r 的点对矩形有限制。令 ui=minxj=i,2yj>wyj,di=maxxj=i,2yj≤wyj:
然后直接经典线段树 + 单调栈维护即可。复杂度 O(nlogn)。
Day 52 PA2019 Szprotki i szczupaki#
显然的贪心策略,每次选能吃范围内的最大的吃掉。如果不能达到 k 就寄了。
所以记目前鲨鱼大小为 s,找到 s 的后继 t(比 s 大的鱼中最小的)。定义吃“一轮”表示在大小为 s 时吃掉某些目前能吃的大小 <s 的鱼以达到 t+1。不妨假设 t<k,如果 s 这一轮无法到达 t+1 就显然无解(目前能吃的鱼的集合不变);否则下一轮结束后 s 至少翻倍(因为接下来能吃掉 t)。
所以我们只关心从 s 开始吃一轮的过程,而过程数是 O(logw) 的,直接模拟每次过程即可。需要查询 s 的后继以及大小在某个范围之内所有鱼的大小之和,还要快速找到最小的满足条件的后缀,可以线段树上二分解决,但由于鱼的大小可以重复,平衡树会好写很多。
复杂度 O(nlognlogw)。
Day |Σ| 序列 seq#
模拟赛里面的题,早上降智没调出来。题意大概就是求区间所有子区间的只出现在子区间内的数的最大值的和。
记录一个数 i 的最左出现位置 li 和最右出现位置 ri,一个数只在 [L,R] 中出现当且仅当 [li,ri]⊆[L,R]。
考虑扫描线,统一处理所有右端点在 p 处的询问。对每个 l,记录 Sl=∑l≤r≤pw(l,r),考虑每次 p 的移动相当于给 Sl 加上 w(l,p)。考虑 w(l,p) 相比于 w(l,p−1) 的变化(如果能快速更新 w(l,p),Sl 相当于对历史求和),相当于求所有满足 ri=p,li≥l 的 i 的最大值于 w(l,p−1) 做比较。
这个过程相当于,先令 w(l,p)=w(l,p−1),然后我们每次插入 ri=p 的所有 i,插入一对 (li,p) 时相当于对 w(1,p),w(2,p)⋯,w(li,p) 前缀对 i 取 max,直接吉司机线段树即可。
Day 33 CF1051G Distinctification#
观察到 3 个比较关键的性质:
- 操作具有可逆性,即一串操作序列可以立即撤销。
- 当新插入一个 (ai,bi) 时,必须连续对 i 进行 1 操作使得不存在 j≠i,aj=ai。
- 当存在 ai=aj+1 时,可以花费 bj−bi 的代价交换 ai 和 aj,如果 bj<bi,交换 ai,aj 一定更优。
所以考虑最后的 ai 在值域上形成的连续段,贪心策略是使每个连续段的 bi 递减。而由于插入一个 (ai,bi) 时,先将 ai 移动到其所在连续段的末尾,然后只需要考虑合并两个连续段,所以连续段数量变化为 O(1),考虑数据结构维护每个连续段的 b 的信息。
首先可以用并查集维护出某个 ai 所在的连续段的右端点。然后在每个连续段的右端点上维护一棵以 b 为下标的权值线段树,线段树 x 中代表区间 [l,r] 的节点记录这个连续段内 cx,l,r=∑[bi∈[l,r]] 和 sx,l,r=∑bi[bi∈[l,r]] 的值。
合并两个连续段 x,y(ly>rx) 时,先将 y 左端点和 x 对齐(即目前答案减去 cx,1,n⋅sy,1,n)。然后只需要考虑将 x,y 中所有元素重排后的最小答案,根据贪心策略进行线段树合并并且计算贡献,类似 cdq 分治,每次考虑 i∈x,j∈y 或者 i∈y,j∈x 满足 bi≤mid<bj 的 bi 跨过 bj 所产生的贡献即可,为 sx,l,mid⋅cy,mid+1,r。
Day Z(Ni) APIO2019 桥梁#
想成 kruskal 重构树后就再也不会了。
考虑没有修改怎么做,将所有边和询问按照权值从大到小排序,对于一个询问 (s,w),向并查集中插入所有边权 ≥w 的边,维护连通块大小即可。
现在有了修改,考虑对询问修改分块。设每 B 个询问修改重构一次并查集,每次仍然将(前面块的修改后的)边权和询问从大到小排序,然后顺序加边,如果一条边被当前块内修改就跳过。
询问 (s,w) 时枚举块内每个修改 (ei=(u,v),w′),如果修改时间在查询时间之前并且 w′≥w 并且是这个询问之前的最后一次修改,向并查集中加入边 (u,v);如果修改时间在查询时间之后并且 (u,v) 本来的边权 ≥w,也向并查集中加入 (u,v)。每次询问之后撤销贡献即可。
复杂度是 O(qB(mlogm+B2logn)) 的。
Day 22+32+42 NOIP2022 比赛#
考虑离线,对右端点 r 扫描线,对每个左端点 l 维护 Sl=(maxi∈[l,r]ai)(maxi∈[l,r]bi)。然后查询就变成了求历史版本和,由于需要维护最大值,建立关于 ai,bi 的单调栈,题目就变成了:
- 令 ai←ai+c,i∈[l,r]。
- 令 bi←bi+c,i∈[l,r]。
- 令 ci←ci+aibi,i∈[l,r]。
- 查询 ∑i∈[l,r]ci 的值。
类似上面那题,没有历史版本和的问题时容易的。维护 ai,bi 的区间和、区间加法标记以及 aibi 的区间和就行了。由于要维护历史版本和,再维护 ai,bi 的区间历史版本和、区间历史加法标记和以及 aibi 的历史版本和、ai,bi 标记乘积的历史版本和即可。
Day XXX Ynoi2012 NOIP2016 人生巅峰#
注意到修改是易于复合的立方操作,而且值域非常小,所以可以直接 O(vlogm) 预处理出对每个 i∈[0,v) 操作了 2j≤m 次的结果,维护出每一位被修改了多少次,查询某一位的值直接倍增 O(logm) 即可。
然后这个限制很弱,因为如果区间内有重复的数的话直接分别选了这两个数,所以当 r−l+1≥v 时就一定满足了。
考虑进一步弱化限制,你发现这东西再看一眼就会爆炸。因为区间 [l,r] 的子集数为 2r−l+1,而子集的贡献和的范围只有 v(r−l+1),则 2r−l+1>v(r−l+1) 时必定满足,所以 r−l+1≥14 时必定满足(事实上很难构造出长度 ≤13 且子集贡献和的个数卡满 2r−l+1 的数据,所以乱搞都能过)。
接下来考虑 r−l+1≤13 的情况,令 fi,j 表示前 i 个数是否能凑出大小为 j 的集合。发现对于一个位置 i∈[l,r] 若 fi−1,j=fi−1,j−ai−1=1 则有解,因为可以直接将 i 塞进后者集合,并且不用担心有交集的情况(如果有交集,两边同时减去交集即可)。
然后 bitset 开冲就完事了。
Day ⌊π3⌋ Luogu5163 WD与地图#
原神答辩缝合怪题目。
先考虑无向图的版本怎么做。套路地,考虑时间倒流,然后就变成了加边、改点权、查询连通块前 k 大之和,线段树合并加并查集维护即可。
现在的边有向,依旧考虑时间倒流,相当于将连通块改成了强连通分量。问题在于只有一条边不一定能使两条边处在同一个强连通分量内,这启示我们对边考虑。
如果能求出每条边 ei(u,v),(u,v) 首次被加入同一个强连通块内的时刻 ti,那么 u,v 就相当于连了无向边,这条边的存在时间区间为 [ti,q],于是就可以转换为无向图的版本。
压力来到如何求出 ti,显然越早加入的边的 ti 越小,那么整体二分即可,每次分治答案区间 [l,r],则加入插入时间在 [l,mid] 的所有边,跑一遍 tarjan,再递归右半边,撤销后递归左半边即可。复杂度两个 log,注意不能遍历到孤立点否则就会爆炸。
Day Mr(O2) THUPC2021 鬼街#
这东西原来叫减半报警器??
首先这题肯定和数论没啥关系,因为 105 之内的 maxω(n)=6,即一个数至多只有 6 个质因子。
然后我们发现如果 x 有 k 个不同的质因子,这 k 个质因子代表的房子内闹鬼总次数达到了 y,那么至少有一个房子内闹鬼次数达到 ⌈y/k⌉。换而言之,检测房子闹鬼次数达到 ⌈y/k⌉ 就是报警的必要条件。
那么我们把每个报警器 (x,y) 拆分成更小的报警器:
- 在 x 的 k 个不同质因子 d 代表的房子上安装一个阈值为 t=⌈y/k⌉+sd 的小报警器 (d,t)。sd 为目前房子 d 的闹鬼次数,当闹鬼总次数达到 t 时激活小报警器。
- 在每一个房子 d 处用一个小根堆来维护这个房子所有的小报警器 (d,t),按照阈值 t 从小到大排序。当这栋房子闹鬼时,就可以从小到大找到所有阈值不超过总闹鬼次数的报警器。
- 一旦有一个 (d,t) 小报警器被触发,意味着其所在的报警器可能已经报警了,直接暴力检查;如果没有报警,对应报警器的所有小报警器 (d′,t′) 重新分配阈值 t′=⌈(y−(sd−t))/k⌉+sd′。
不难发现以上的分配方案下,一个报警器至多被重新分配 log6/5y 次。
复杂度 O(ω(n)mlogmlog6/5y)。
Day Sfib(7) NOIP2021 棋局#
历时 5.5h 写 + 调,真的有人会在场上写正解吗。/oh/oh
考虑以某些同种种类的边组成的若干连通块,注意到放一个棋子可能会将棋盘分割成不同的连通块,于是倒序考虑将分裂变成合并,每次相当于删去一个棋子。
(x,y) 的答案分四类讨论,分别为 S1,S2,S3,S4:
- S1 为 (x,y) 能通过 2 类边到达的所有点个数。
- S2 为 (x,y) 能通过 3 类边到达的所有点个数。
- S3 为 (x,y) 分别通过 2 类边和 3 类边都能到达的所有点个数。
- S4 为 (x,y) 通过 1 类边能到达的,通过 2,3 类边均不能到达的点的个数。
答案就是 S1+S2−S3+S4。考虑求出以上答案所需要维护的信息,注意到我们要支持合并的数据结构,定义 (x,y) 的横向坐标为 (x−1)m+y,纵向坐标为 (y−1)n+x,则一行的子段横向坐标连续、一列的子段纵向坐标连续:
- S1:对每行/每列建并查集维护其中 2 类边组成的连通块,由于只能走水平/竖直方向,每个连通块在横向/纵向坐标中是区间的形式,记录每个区间左端点以及右端点即可。
- S2,S3(因为有些限制重复所以放一起了):对每个点建 4 棵线段树,维护这个点所在的 3 类边连通块(这里 3 类边 (u,v) 联通当且仅当 u,v 上均没有棋子),分别表示按照横向坐标为下标的连通块中没有棋子的点集、纵向坐标为下标的连通块中没有棋子的点集、等级为下标的连通块相邻的有白棋的点集、等级为下标的连通块相邻的有黑棋的点集。
- 由于 S4≤4,我们直接对于 (x,y) 的上下左右四个点用以上维护出的信息暴力 check 一遍就行了。
复杂度 O((nm+q)lognm),但是常数巨大无比。
Day 叁拾肆 CF1548E Gregor and the Two Painters#
DS 写不动了,标题也取不动了www。
类似 Day 1 CF1270H Number of Components,每个连通块中选出一个代表的点。令一个连通块内所有点按照 vi,j={ai+bj,i,j} 排序,对最小的 vi,j 计数。于是相当于求多少个格子不能走到另一个(非严格)三位偏序它的位置。
令 pi=maxj<i,aj≤aij,qi=minj<i,aj<aij,如果极值不存在可以特判,那么 vi,j 被计数当且仅当它不能走到第 pi 行或第 qi 行,因为显然有 api+bj<ai+bj。
大眼观察,发现 (i,j) 能走到第 pi 行当且仅当能走到 (pi,j):首先充分性是显然的,然后考虑证明必要性:
不妨设 pi<i−1。如果能从 (i,j) 走到第 pi 行,最优秀的贪心方案是先从 (i,j) 走到一个点 (i,j′),满足 (i,j′) 是 (i,j) 在第 i 行能走到的 bj 最小的点(显然不会先移动第一维,因为 ∀k∈[pi+1,i),ak>ai),然后从 (i,j′) 一举走到 (pi,j′),然后不难发现由于 api≤ai,所以 (pi,j′) 是一定能走到 (pi,j) 的。
又由于我们现在对 vi,j 计数,所以 j 一定是 (i,j) 在第 i 行能走到的最小的 j,也就是 j′=j。所以我们只需要判断是否能直接从 (i,j) 走到 (pi,j),即 maxk∈[pi,i]ak+bj≤x。对列同理,于是用单调栈求出 pi,qi,最后变成若干个限制的问题,化简后二维数点即可。
复杂度 O(nlogw)。
Day (2B)12 Luogu5111 zhtobu3232的线段树#
题面明示线段树。
每个线段树节点维护 px,sx 表示这个线段树节点代表的区间可以拼出的前缀/后缀数,那么一个节点的贡献就是 sl(x)pr(x)。合并左右儿子的信息时要注意讨论 x 是否非法、左右儿子是否非法。
修改查询的时候动态开点,如果一个点是空的且没有被修改直接贡献 12len(len+1) 即可,len 为区间长度。
复杂度 O(mlogn)。
Day 62 SHOI2013 发牌#
平衡树板子,只是随机跳题跳到了。
对下标序列建平衡树,每次分裂出一段前缀拼到结尾即可。
复杂度 O(nlogn),但是跑得好慢啊。
Day −i(1+6i)(6+i) CF1479D Odd Mineral Resource#
前几天模拟赛有一个思路类似的。
出现次数奇偶性相关的问题,我们一般把颜色 c 映射到随机权值 wc∈[0,2k),然后进行异或哈希。
利用主席树很容易通过预处理,O(logn) 得到 (u,v) 两点之间路径上所有点 x∈[u→v] 颜色在 [l,r] 之间的权值 wcx∈[l,r] 的异或和,即:
如果 f(l,r) 这个值为 0,我们可以判定路径上所有 [l,r] 之间的颜色都出现了偶数次。那么我们可以二分答案,设目前二分颜色区间为 [l,r],如果 [l,mid] 之间有出现次数为奇数的颜色,那么满足 f(l,mid)≠0,否则 f(l,mid)=0。
这样就做到了 O(nlog2n)。然后我们发现这个过程可以类似区间最小值一样,在主席树上进行二分,于是复杂度就变成 O(nlogn) 了。
Day P21+P22+P23 Luogu5108 仰望半月的夜空#
不考虑左端点最小,如何求出一个字典序最小子串,只需要建出后缀数组后找最小的 i 满足 n−sai+1≥L,然后取 S[sai,sai+L−1] 即可。
现在的问题在于可能存在一个 j>i 使得 S[sai,sai+L−1]=S[saj,saj+L−1],并且 sai>saj,这样的话 S[saj,saj+L−1] 的左端点就更小了。
但是注意到 sa 是按照字典序排序的,也就是说,lcp(S[sai,n],S[saj,n]) 在 i 固定时随着的 j 递增而单调不升,那么合法的 j 是一段以 i 为左端点的区间 [i,p]。如果我们求出了这个区间,我们就查询 k∈[i,p] 的 sak 的最小值即可。
由于两个后缀 S[sai,n],S[saj,n] 的 LCP 长度相当于 mink∈[i+1,j]heightk,所以处理出 height 数组后建 ST 表支持 O(1) RMQ,然后直接二分右端点 p 找到最小的 p 满足 lcp(S[sai,n],S[saj,n])≥L 就做完了。
复杂度 O(nlogn)。
Day 31+32+33 蛤膜(hamo)#
10/28 模拟赛 T4。
绝对众数显然进行摩尔投票。
和 这题 类似。需要维护的运算不可差分,考虑分治进行二区间合并。
对行分治,设分治到区间 [l,r],处理所有满足 lx≤mid<rx 的 (lx,rx,ly,ry) 询问。可以 O(m) 对 [l,r] 之间每一行建线段树,下表为列,线段树区间 [L,R] 维护 (x,mid,L,R)/(mid+1,x,L,R) 矩阵的摩尔投票结果。查询可以二区间合并,然后就是 O((nm+q)logn) 的了。
众所周知摩尔投票在没有绝对众数的时候会发电,表现为任意输出一个不合法数。所以要验证答案。按照 ans 分类,不难发现每一类验证相当于若干单点加矩形求和,而且询问全部在修改后面,相当于二维偏序,再次进行离线即可。
复杂度 O((nm+q)logn),由于我是懒狗,验证答案没有进行离线而是使用二维树状数组,复杂度 O((nm+qlogn)logn),跑得很快啊!
Day XL CF1696G Fishingprince Plays With Array Again#
设 i 位置进行了 xi 次一操作、yi 次二操作,写出线性规划:
对偶一下就变成了:
设 X≤Y,用调整法可以证明 zi 取值只能在 {0,1X+Y,1Y} 中。直接 (min,+) 矩阵乘法维护即可。复杂度 O(k3(n+qlogn)),k=3。
Day P1+P2+P3+P4+P5+P6 JOISC 2021 Day3 保镖#
放到二维平面上考虑,点 (x,y) 表示 x 时刻在 y 位置上,那么第 i 顾客的路径可以看成起点为 (ti,ai),终点为 (ti+|bi−ai|,bi) 的线段 Pi。
注意到一个保镖的最优策略中一定不会闲着不动,因为如果他此时正在保护一个人的话显然跟下去比不动更优;如果他此时没有保护一个人的话显然是去找下一个保护的人更优。所以保镖的路径形如若干斜率 1 或者 −1 形成的线段拼接起来的折线 Qi。
考虑到一个保镖一旦离开了正在保护的人就再也追不上他了,那么保镖 i 保护顾客 j 时间为 Pj 和 Qi 重合的线段在时间轴上的投影长度,也就是重合线段长度 l 的 1√2。考虑将坐标轴顺时针旋转 45 度再进行放缩,那么 (x,y)→(x−y,x+y),此时重合线段长度 l′ 为 √2l,那么保护时间就是重合线段长度的 12,收益就变成了 ci2⋅l′。于是令 ci←ci⋅12。
于是问题转化成平面上有若干平行或垂直于坐标轴的直线 P1,P2,⋯,Pn,q 次询问,每次询问给出一个点 (x,y),每次只能向上/向右走单位距离,和 Pi 重合的长度为 Li,收益为 ci⋅Li,求总收益最大是多少。
所有顾客线段延长交叉起来会变成一个大小为 O(n×n) 的网格图。考虑 (x,y) 一定是先径直走到网格图的某条线上,沿着某条可能不完整的线再走到某个格点,最后再在格点之间移动。
对网格进行离散化之后容易 dp O(n2) 预处理出 fi,j 表示目前在网格上的 (i,j) 点,对应坐标系中的 (xi,yj) 点(1≤i≤m,1≤j≤k),只能向上/向右走,走下去的最大收益。然后假设 (x,y) 向上走到了 (x,yj) 所在的一条边,再向右走到了第一个格点 (xi,yj),那么取经过 (x,yj) 的 ci 最大的线段 Li,收益就是 ci(xi−x)+fi,j。
注意到经过 (x,yj) 的线段必定经过 (xi,yj) 和 (xi−1,yj),因为一条直线起码经过两个格点。所以记 vi,j 为经过格点 (xi,yj) 且 (xi,yj) 不为其右端点的线段 Pi 中 ci 的最大值,那么收益就是 vi−1,j(xi−x)+fi,j。
注意到 x 固定时 xi 为第一个大于 x 的格点横坐标,所以 i 是固定的,将 vi−1,j 看作一条直线的斜率,fi,j 看作截距,问题转化为求给定 k 条直线在 xi−x 处的 y 坐标的最大值,离线下来李超树解决即可。
复杂度 O((n2+q)logn+qlogq)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话