record 12.11-12.17
arc196 D
场上读错题了=_=,当成每次只能选连续 \(m\) 个。
首先我们考虑转化一下等价条件,看看什么样的序列是可以被加出来的。
\(A\rightarrow B\) 要求:
-
\(\forall A_i\leq B_i\)
-
\(m|\sum (B_i-A_i)\)
-
\(\forall B_i-A_i\leq\frac{\sum(B_j-A_j)}{m}\)
这都是比较显然的。然后我就不会了。
这个时候解的形式比较多样化,难以操作。但我们要的是最优解,考虑找到最优解一定具有的形式。
发现最优解的 \(B\) 一定是 \((x,x+1,\dots,x+n-1)\) 的排列。我不会证,之后再补。
upd:看懂了 https://www.cnblogs.com/ying-xue/p/arc169.html。 但是还是感觉不出来。
后面随便做做就好了。
Fun:等价条件转换,对最优解加强限制。
abc332 G
没打,赛后补的。
题意是给一个 \(n\times m\) 的方格网,每一行、列的和都有限制,每个格子的值也有 \(\le i\times j\) 的限制,问总和最大多少。
这个第一冲动是网络流对吧,可惜跑不动。那就手跑一下。
我们建 \(S,T\) 点,以及 \(A,B,C\) 类点分别表示行、格子、列的限制。
最大流不好想,我们不妨把最小割写出来。发现格子边怎么割只跟它所属行列的分割方式有关。
我们记 \(S\) 为 \(A\) 类点当中属于 \(S\) 的,\(T\) 为 \(B\) 中属于 \(T\) 的。观察这个式子比较独立,可以考虑枚举一部分。
左右两部分无关,拆开来,左边部分可以 \(O(n^3)\),右边注意到 \(\min\) 的取值随 \(p\) 增大每个只会切换一次,可以做到 \(O(m+n^2)\)。
总复杂度 \(O(n^2)\)。
Fun:网络流建模,枚举独立两部分中的一个。
CF1904 div2 E
树状连通块内到一个点最远的点肯定是直径的端点。倒着做,找到一个合适的顺序。
找不到,我们可以每个点都恰好在某个询问中被问过一次。
考虑每个点删了之后干了什么,我们发现会有若干次 ban 一个子树,只有一次 ban 一个子树补。
那现在这就变成一堆 dfn 上的区间了对吧,我们把问题转化成了 \(\sum k\) 次询问 \([l,r]\) 到 \(x\) 的最大距离,这个好像很典,让我想想。
那我们扫描 \(x\) 这一维,考虑一次移动对 \(dis\) 的影响,发现就是区间 +1 区间 -1,那是不是随便做一做就好了啊。
BTW,想着想着 NOI 名额分配出了,HA 7 个队/ng。唉唉,写题去了。
CF1904 div2 F
又读错题了=_=。
注意到每个限制都可以转化成一个点的值 \(\geq/\leq\) 一个链上值,考虑建图来表示,最后拓扑排序,问题变成加速建图。
显然我们树剖,然后额外 \(O(n)\) 个点,表示重链上 前/后 缀 \(\min/\max\),就可以做了。
这个看起来就麻烦,看了一眼 sol 里给的是倍增做法,稍微好一点点。不打算写了。
BTW,感觉这场有点简单,没打亏死了。
Luogu P9915
有点意思。
第一眼觉得很困难啊!但是稍微观察一下发现这个东西是上下反对称的,所以询问下半部分就完全等价于询问对称位置。
但是这个时候还不完全是子问题,我们缩了一半的行,还要再缩一列。这个是 trival 的。
做完了。
Luogu P9916
想到了每次覆盖前一个,想到了分治,但是不会构造每次覆盖前一个的方案,寄寄寄。
首先我们发现,我们可以通过,把左右两半分别当成一个数,来判定负数在哪边,这样把需要判定的数缩到了 \(O(\log n)\)。
接下来考虑怎么判定。第一次肯定这个数本身就可以了,第二次可以加上 \(v_1v_2\),这样就把 \(v_1\) 的影响消除了。
但到第三次,就只能加 \(2v_1v_2v_3\),以此类推,可以解决这个问题。
对于判定部分,同样有另外一种想法。
我们考虑如何给 \(Q\) 乘上一个数。考虑令 \(Q\) 始终是 \((v_1+1)(v_2+1)\dots\) 的形式,然后要乘一个 \((v_k+1)\) 需要枚举前面的子集,\(2^{k-1}\)。
BTW,为什么可重集的条件没有用上呢 好像用上了。而且我比较好奇 spj 怎么写。高精?
CF364 E
发现 \(k\) 很小,可能有用。
先看看能不能乱搞做,不太行。
考虑分治,两个维度轮着来。我们先横着切一刀,统计跨过中间的部分。考虑枚举左右端点,然后转化为一维问题,这个显然可以双指针对吧,但是现在是三次方,想怎么用单调性降一个,卡了很久。
发现我们转化成一维问题之后其实并没有利用跨过中间这个性质。猛然想起 \(k\) 很小,分别记录上下半部分和为 \(p\) 有几个可以 \(O(k)\) 合并。我们现在会平方了。
然后我发现这个复杂度我不太会算,但是感受一下大概是对的。写一发试一下。
事实上,我们要两个维度轮着来,是因为我们单次的复杂度是 \(O(n(n+m))\) 这类东西,倘若是 \(O(nm)\),其实只对一个维度一直分治就是对的了。
upd:写完了,175 lines,调了一上午,要死了。
CSTC 2016 时空旅行
套路世界大战。
题目看上去就比较割裂,我们先考虑后面那部分。
你有一堆点 \((x_i,y_i,z_i,c_i)\),以及一个坐标 \(x_0\),求 \(\min\limits_{y,z,i}(x_0-x_i)^2+(y-y_i)^2+(z-z_i)^2+c_i\)。
第一眼看到的时候我很震惊啊,这个 \(y_i,z_i\) 看起来是来卖萌的。然后写一下式子就变成了一个斜率优化的典题。现在我们的问题变成怎么在删/加点的同时维护这个凸包。
你发现加点很简单,删点很讨厌,考虑怎么干掉它。
你考虑依赖关系构成一棵树,dfs 序搞下来,然后线段树分治就做完了。
复杂度 \(O(n\log^2)\)。
看了看题解,我们可以做得更好。首先询问的时候可以排序,然后考虑插入操作怎么做到有序。这个看起来不好保证,因为我们插入之后在线段树上可以顺序奇奇怪怪(所以才要求交换律),但是在线段树单个节点内部是有序的。所以我们可以把一次询问拆成 \(\log\) 个,就可以了。
BTW,你也可以说这个东西是拍成 dfs 序搞下来之后,把操作拆成若干个插入删除之后,做 CDQ 分治。
loj 6469
然后我们在 Trie 上随便做一做就可以了。
WC2010 重建计划
这个题之前用点分治写过了,我们现在用长剖重新想一下。
首先我们来一个 \(dp_{u,x}\),想一下合并,很好合并啊!
然后想一下怎么统计答案,发现我们可以承受枚举短链的代价,在把短链合并到长链的同时统计就可以了,但是要询问区间最值,所以扔到线段树上。
额,做完了。再带上一个分数规划,复杂度 \(O(n\log^2n)\)。
Luogu P2578
给一个序列,支持单点修改,区间询问能否构成公差为 \(k\) 的等差数列,强制在线。
我们找个充要条件:
-
\(mx-mi=(r-l)k\)
-
\([l,r]\) 数两两不同
-
\(a_l\equiv a_{l+1}\equiv\dots\equiv a_r(\bmod k)\)
第一个很好办,第二个套路地维护一个 \(pre\) 就可以了。
第三个可以转化成相邻两项的差是 \(k\) 的倍数,我们维护差的 \(\gcd\) 就可以了。
做完了,可能要 \(\log^2\)。
Luogu P5069
先找这个答案到底等于什么。观察它的行为,发现每次是把全局最大值找出来,把它和左右干掉,造成最大值这个贡献,然后就变成了两个独立的子问题。
这玩意没用啊,再想想。
感受了一下,感觉单点修改带来的影响不会很大。假了,你考虑中间一个高的两边往下减,然后你把中间这个改的很小。
读错题了!!!!!!!!!!!!!!!!!!!!让我重新想想。
我们本来每次能删三个,现在我们考虑操作造成了多少浪费。
等等,这两个想法是不是等价的。好像能证。
好吧,兜兜转转又转回来了。我们考虑怎么计算这个,你考虑一个点在以它为最大值的区间里,向它左右区间的最大值连边。这个其实就是把最开始的那个东西画了出来。考虑我们能不能在修改的同时维护这个东西。
看题解了。
题解是考虑的单调段是隔一个选一个,所以维护极值点即可。很有道理。
另外一种解法是线段树。我们最开始说可以拆成子问题,但是这个必须从最大值处拆开,不是很好。观察发现哪怕从别的地方拆开,影响也不会很大(最多 1),所以考虑用线段树维护之后合并。感觉我没触及本质。
Luogu P5608
一眼线段树对吧,发现操作一不太好做。
现在要么想方法在修改的同时算出来,要么不算,通过一些神奇的方式延后计算。
显然第一条路更合适一些。每个节点维护一个多项式类的东西,观察到只有 \(O(\sqrt{len})\) 种不同的次幂,我们可以快速幂算出当前的值,复杂度 \(O(\sqrt{len}\log len)\),但是这是 Ynoi,所以无法通过。
考虑光速幂,发现卡了空间开不下。通过学习题解,发现一种做法:我们对要算的次幂排序,然后相邻两项快速幂转移即可。
假设是 \(p_1,\dots,p_k\),我们有 \(\sum p_i=len\),记 \(c_i=p_i-p_{i-1}\),那么有 \(\sum ic_i=len\)
而 \(n!\sim\sqrt{2\pi n}(\frac{n}{e})^n\),所以上式改写一下,求个导,最大值 \(\le\sqrt{len}\),证完了。
这样我们单个点的复杂度变成了 \(\sqrt{len}\)。对于一次修改操作,复杂度是 \(O(\sqrt{n})+2(O(\sqrt{\frac{n}{2}})+O(\sqrt{\frac{n}{4}})+\dots)=O(\sqrt{n})\)。
同时还可以提一句的是,倘若我们开 vector 常数有点大,可以采用对每个点,如果它的最大幂不到根号,开数组就行了,超过根号的,存在 vector 里(?直接开一个 vector 不行吗)
UOJ 228 & LOJ 6029
同一类型的。
首先不带加操作是典中点题,我们接下来考虑分析加操作带来的影响。
我们定义一个线段树上节点的势能函数,是这个区间的极差的 \(\log\)。
最初的势能是 \(O(\log(nV))=O(\log n+\log V)\)
考虑一次 除/开根 操作之后,这个极差会怎么变,发现至少除以 2,并且为 0 之后,就可以对整个区间直接操作无需暴力。
一次操作会使这个区间内的线段树节点势能 -1,代价是势能减少量。
一次加会把 \(\log\) 的区间的势能最多变成 \(\log V\)。
所以总复杂度是总势能,\(O(n\log n\log V)\)。
关于 CDQ 分治和二进制分组
你把它们都扔到分治树上来考虑,发现 CDQ 是每次处理同层的两个兄弟之间的贡献,二进制分组是类似于树状数组一样,每次处理前面的区间对某一个位置的贡献。
虽然说二进制分组是在线算法,但事实上它的转移顺序被固定死了,你比方说处理 1D/1D 动态规划问题,加上三维偏序的限制,你发现其实二进制分组不太好做。
而 CDQ 分治,它的转移顺序比较随意,只要把可能造成贡献的都转移过,就可以按任意顺序转移。你比方说还是上面的问题,CDQ 分治的时候,我们会把这个区间重新排序,其实就是更改了转移顺序。脱离这道题,如果你乐意的话,你可以先转移同层兄弟间的贡献,再搞下一层的。
upd:其实很显然,因为强制在线相当于锁死了二进制分组必须要按时间轴的顺序进行,而 cdq 分治没有这个限制。
cdqz 数据结构 1021
一棵树,两个操作,一个是把一个点变成关键点,一个是询问一个点到最近关键点的距离。
离线下来,把每个点变成关键点的时间记录下来,然后按时间顺序重新编号,询问操作变成了每个点到一个区间距离的最小值。这个典中点前几天刚做过。(虽然刚刚没想出来)
upd:上面的做法假了,因为要求标号必须是 dfn。
我们考虑建出点分树,然后一次修改操作相当于在它点分数上祖先处激活它,一次查询操作相当于查询这个点到自己点分树上祖先的距离加上已激活的点到这个祖先的距离,这个是非常好做的。有空写一下。
Ynoi2007 rgxsxrs
不难发现这个操作会使区间的极差减小,并且所有数都是单调不增的,所以这个题很可能要均摊。
我现在是想了一个极差减 \(\min(x,des-x)\) 消耗 \(\min(x,des-x)\) 的做法,由于 tag 难以合并,外部结合一个分块就可以做到 \(O((V+n)\sqrt{n})\)。也许能通过 \(53\%\) 的数据。
现在我不会,等以后再看吧。
cdqz 数据结构 1023
一棵树,支持三种操作:
-
0:修改边权。
-
1:更改某个点的激活状态。
-
2:查询到某个点距离最远的激活点的距离。
我们考虑跟 1021 类似的做法。那么操作 1、2 都是好做的。考虑怎么解决操作 0。
观察到一条边在一次分治的过程当中只会进入到一个子分治区域中,这说明这个边在点分树上对应的点是一条从根到叶子的路径,我们只需要考虑修改这些点就可以了。
仍然是考虑一次分治过程,发现会影响到的点是一个子树,那我们 dfn 拍平下来,建线段树,每次相当于是区间加。
但是要注意,不同的分治过程对应的 dfn 序都不一样,所以一个点在它点分树的祖先当中可能有很多不同编号。
复杂度 \(O(n\log^2n)\)。
Luogu P4062
给序列,统计有绝对众数的子区间数。
这个绝对众数我之前总结过一次,现在还依稀记得一些。
我们只写有用的性质。首先,对于一个有绝对众数的区间,你从任意位置劈开,两个区间至少一个绝对众数还是这个数。这个非常显然。
然后,对于以一个确定位置 \(p\) 为一个端点的区间,绝对众数的种类数至多 \(\log\) 个。
这个你考虑填数让绝对众数改变,你发现你分别要填 \(1,2,4,8,\dots\) 才能改一下,所以是 \(\log\) 个。
这个题我们已经做完了。你考虑一个分治算法,每次从中间劈开,先递归下去,然后考虑合并,这个你只需要枚举可能成为区间众数的数,这是 \(2\log len\) 种,然后对每个数,把它变成 +1,别的变成 -1,算一下前缀和,就行了。
复杂度是 \(O(n\log^2n)\)。
这个东西可以稍微加强一丢丢:记区间 \((l,r)\) 的所有子区间满足条件的有 \(f(l,r)\) 个,我们可以询问 \(\sum\limits_{l=1}^n\sum\limits_{r=l}^n f(l,r)\)。挺没意思的,写一下系数就行了。
看了一眼题解的 1 log 做法,感觉没啥用。不如这个启发性强。
看了 Alex 的线性做法,没看懂=_=。
Luogu P5073
给你一个序列,以及若干次查询操作,每次查询把整个序列加 \(x\) 后 \([l,r]\) 的最大子段和。询问独立。
我们肯定先考虑最大子段和怎么做的对吧,那经典做法就是线段树来维护分治结构,左右子节点维护前后缀最值,然后合并。
现在问题出在哪呢?你对序列修改之后,这个前后缀最值会改变对吧,合并之后的结果也会变。
有一个想法:遇到这种会变的,你就把它所有将要取到的值列出来,相当于是把一个动态的过程给静态化了,这样非常有助于思考。
那我们看看一个区间的前后缀最值有哪些可能的取值。仔细思考发现这个东西是一个凸包,我们每次用直线去切然后取截距就可以了。
那我们不妨真的维护这个凸包,所有凸包的总节点数是 \(O(n\log n)\) 的。
那我们自然地,考虑把询问按 \(x\) 排序,然后看凸包怎么随 \(x\) 的变化而变化。你发现每次变化都是删掉一些点,而总节点数是 \(O(n\log n)\),所以这部分复杂度也一样。
你在凸包上删掉一个点之后,会修改这个节点的前后缀最值,进而影响它祖先的合并后的答案,一次影响 \(O(\log n)\) 个。所以总复杂度是 \(O(n\log^2n)\)。
看了一眼题解说过不了,让我再想想。
好吧,看了题解,其实每个节点的最大子段和也是一个凸包。我们可以先对左右子节点的凸包进行合并,然后再求左右子节点前后缀凸包的闵可夫斯基和。
同时注意到这个东西卡空间,不过这个好办,我们把询问先拆到线段树上,然后再一个个节点地做就行了。
然而我并不想写这么麻烦的东西
让我先学习一下闵可夫斯基和。
Ynoi2015 我回来了
考虑一次亵渎 \(d\),首先会触发一次,然后如果有 \((0,d]\) 的,再触发一次,如果有 \((d,2d]\) 的,再触发一次,以此类推。
那你发现我们关心的区间总数其实也就 \(O(n\log n)\) 个,你的一次询问相当于是做一个类似于 mex 的东西。
但是你发现想要均摊需要一些精细的实现。也就是说,每个区间只能在它要被删除的时候再访问到。
那我们考虑建出一棵线段树,把这些区间扔上去,这一共是 \(O(n\log^2n)\) 个,然后每次激活一个位置的时候,就访问从根到叶子的链,把这个链上所有区间全部激活,并删除即可。
总复杂度 \(O(n\log^2n)\)。
Luogu P3586
我们肯定先考虑怎么去判定。
你把选的方案看成一个 \(s\times c\) 的矩形,往里填充数,每行不能相同。
你考虑一个数,比方说被选中过多次,但是不一定连续选中,那么我们一定可以改成连续的。
一个数,如果被选中过,但是没有选干净,那只要这个数被选的次数不超过 \(s\),那我们可以把它选干净(虽然这个时候会有别的数选不干净),这说明一个数要么被选干净,要么它这一列要超过 \(s\) 了。
所以现在问题就变成了,你能不能把这些数分成 \(c\) 组,使每一组的和都超过 \(s\)。
我们猜测有一个贪心方案:每次拿出最大的,然后塞到当前和最小的那一组里头。
看了题解,你发现我们把超过 \(s\) 的削成 \(s\) 之后,剩下的排成一行,你从左往右,如果左边那个不满,就把右边这个往上面补,这样每个数都会被利用,所以只要和满足条件就是满足条件的。
CF453 E
首先我们记第 \(i\) 个数上次改成 \(0\) 是在 \(t_i\),并且增长满需要 \(n_i\) 时间。
对于时刻 \(t\) 的操作,我们就是查询 \(t_i+n_i\le t\) 的部分的 \(\sum m_i\),以及剩下那一部分的 \(t\sum r_i-\sum t_ir_i\)。
你发现这样要树套树,还难以 pushup。
但我们发现每次操作结束之后,都有 \(t_i\leftarrow t\),这个就有个颜色段均摊了。
对每个颜色段,我们可以在内部二分求出答案。
题解区还有做法,分块,预处理每个块经过若干时间后的贡献。也可以,区别不大。为了偷懒,我就写分块了。
CF702 F
首先甭管有没有用,先扔到二维平面上,便于观察行为。
然后你发现每次都是在一个半平面内选一个最高的点。那显然,一个又贵又差的东西永远不会被选中,那我们现在得到了一个升序序列。
接下来我们感觉一下,一个人买的种类数不会太多。具体而言,这是因为一个人如果买了一种 T 恤,那么他的钱数会变成取模后的结果,也就是说,最少除以 2,所以一个人至多买 \(\log V\) 种。
那我们对每个人,模拟就好了。
=_= 读错题了。每个人每种衬衫最多只能买一件。
那我们考虑把所有询问一起处理。这样对于一个衬衫而言就是比它大的减,比它小的不动。
可以使用平衡树维护,重合部分直接暴力插入即可。
另外,之前看到一种可以实现这个东西的方法,我们写一下试试。
upd:这俩东西说的是不是一样的啊。
关于上面这个说的我们给一个证明。
你有若干个序列,初始每个序列只有一个数,现在要求你支持如下操作:合并两个序列,把一个序列按照某个值切割,给一个序列全局加,查询单点值。
这个东西我们考虑用平衡树来维护,合并的时候,split 出重合的部分,不重合的部分直接合并,重合的部分暴力 insert。
证明考虑定义势能是一棵平衡树上,每个点到左右最近的点的距离的 log 的和。
这样,一旦你暴力插入一次,两个距离一定有一个会减半,也就是说,势能减 1,或者和一个值一样的合并起来,而这种东西不会超过 n 次。而别的操作,只有切割可能会给势能加一个 log,并且总势能是 log 的,所以总复杂度是 2 log。

浙公网安备 33010602011771号