FNの题 Ⅰ

P3227

一种妙的建模方法。首先可以看成是许多条链,然后每个链上只能选择一个点;转化成一条链,相邻点的边权为原图上对应点的点权加上 inf,这样一来就能保证我们只会去割掉一条边了。然后问题就变成了如何保证某两列选择的点绝对值不超过 \(d\) 呢,只需要从 \((x,i)\)\((y,i-d)\) 连边即可,因为如果 \(x\) 断的边的位置大于等于 \(i\) 并且 \(y\) 断的位置小于 \(i-d\),那么图仍然会形成通路,就只能再断一条边显然不优,所以这样连边可以保证 \(d\) 的限制。综上,上面那样的建图可以满足题目中所有要求,跑一遍最大流求最小割,然后模上 inf 就是最后的答案。然后就是不知道为什么当前弧优化和深度优化同时加上去会 T 掉,大概率是我 dinic 写得太劣了。

P3327

\[\begin{aligned} &\sum\limits_{i=1}^m\sum\limits_{j=1}^nd(ij)\\ =&\sum\limits_{i=1}^m\sum\limits_{j=1}^n\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)=1]\\ =&\sum\limits_{x=1}^m\sum\limits_{y=1}^n\varepsilon(\gcd(x,y))(m/x)(n/y)\\ =&\sum\limits_{x=1}^m\sum\limits_{y=1}^n\sum\limits_{g|\gcd}\mu(g)\dots\\ =&\sum\limits_{g=1}^m\mu(g)\sum\limits_{x=kg}\sum\limits_{y=tg}(m/x)(n/y)\\ =&\sum\limits_{g=1}^m\mu(g)\sum\limits_{k=1}^{m/g}\sum\limits_{t=1}^{n/g}\frac{m}{kg}\frac{n}{tg} \end{aligned} \]

思考如何合并计算后面那一堆东西。

\[\sum\limits_{k=1}^{k'}\sum\limits_{t=1}^{t'}\frac{t'}{t}\frac{g'}{g} \]

也就是对于相同的 \(k',t'\),这一堆玩意是相同的,于是直接预处理和数论分块即可。

P4887

二次离线莫队,但其实感觉就是经过小优化的莫队。这道题的瓶颈就在于不好计算一个点对于一个区间的贡献。然而发现其实可以通过差分的方式来计算,把所有需要的询问存下来,最后离线计算即可。然后这个过程就被称为二次离线莫队。而由于需要卡空间,可以把一串询问压在一起进行查询。

P5047

也是二次离线莫队的板子题。发现端点移动产生的变化形如 \((x,l,r)\),表示 \([l,r]\) 中大于 \(x\) 的个数。显然可以差分,然后把那些特殊的询问离线下来,按 \(pl\) 排序之后询问。由于加入数的次数比较少并且查询的次数比较多,所以为了平衡复杂度采用了插入 \(O(\sqrt N)\) 查询 \(O(1)\) 的方法。具体而言就是离散化之后分块,插入的时候更新所在块的前缀和和块与块之间的前缀和即可,复杂度 \(O(N\sqrt N)\)。更低的复杂度真的有去学习的必要吗。

P4119

分块。查询方面直接值域分块就可以做到根号,于是问题就变成了如何快速修改的问题。如果保证所有的修改都是整块整块进行的话问题就显得非常简单了,只需要对于每块维护一个并查集即可;但是实际上可能出现散块的情况,如果直接修改的话就无法正常还原出实际的元素,所以呢散块部分需要暴力重构(直接做的话复杂度其实已经正确了,但是据说会被卡常到 \(60\) 分左右)。为了减小常数,发现散块部分只需要重构 \(x,y\) 的并查集关系就可以了,于是足以通过本题。

P4117

感觉是个非常喵喵的方法。对于每一块,记当前块中最大值是 \(k\),然后和当前修改数 \(x\) 进行比较。如果 \(k<x\) 直接跳过不管;如果 \(k< 2x\),那么暴力把所有 \([x,k]\) 的数减去 \(x\);如果 \(k\ge 2x\),那么就先把 \([1,x-1]\) 中的数加上 \(x\),然后打减标记即可。散块直接重构,然后整块修改的时候用并查集维护即可,为了卡空间可以把所有操作离线下来,每一块分别处理即可。至于复杂度分析需要结合值域思考,两种操作的复杂度相当于都是 \(O(x\alpha)\),而每次操作后最大值都会减掉操作数,所以复杂度是正确的。

有一些需要注意的东西,不然我也不会调一天才调出来。并查集部分和上一道题一样要以位置为基础建立并查集,这样的好处是初始化会简单一些。然后就是要注意下标之间的相对关系,比如假设一个点在并查集中的值是 \(c\),那么它实际的值其实是 \(c-del\),然后要以这个玩意为下标进行上述的区间遍历,初始化的时候也是按照这个下标来处理。然后就是数组不知为什么要开大一点(调了两个小时),以及一些其它的细节。很多细节只能用玄学来形容。

P5046

我看的是 \(O(N\sqrt N)\) 的算法。首先维护 \(pre_x,suf_x\) 代表对应块 \(x\) 对应的前缀和后缀中逆序对的个数,可以用数据结构快速求得。然后维护 \(f(x,y)\) 表示块 \([x,y]\) 中的逆序对个数,这个可以快速容斥维护,复杂度是 \(O(N\sqrt N)\)。最后维护 \(g_{x,y}\) 表示原数组 \(x\) 前缀和第 \(y\) 块的逆序对数量。计算答案时分类讨论一下,如果在同一块,直接用 \(pre_r-pre_{l-1}+merge([bg,l-1],[l,r])\) 即可。如果不在同一块,那么左散块和右散块可以直接计算,中间整块也可以计算,然后对于每一块可以利用 \(g\) 数组计算这一块和两边散块的贡献,最后两个散块之间的逆序对可以一遍归并解决。据说需要卡一下常。确实非常卡常。

P6774

知名神秘题目。和第一篇题解一样的方法,用 \(rk(x,y)\) 表示第 \(x\) 块中排名为 \(y\) 的数,\(p(x,y)\) 表示 \(x\) 对应块前缀中排名小于 \(y\) 的数量,\(pre(x,y)\) 表示前 \(x\) 块中不大于 \(y\) 的个数,\(lsh(x,y)\) 表示块 \(x\) 中不大于 \(y\) 的最大排名,\(c(x,y,z)\) 表示第 \(x\) 块中排名前 \(z\) 个数和前 \(y\) 块里的元素形成的顺序对个数,\(sum(x,l,r)\) 表示块 \(x\) 排名 \(l\)\(r\) 形成的顺序对个数。然后分别看怎么求这些数组。

\(rk\) 只需要排序一下即可,同时可以顺便求出 \(lsh\)\(p\) 做一次二维前缀和就可以了。\(pre\) 也是用二维前缀和去做。\(c\) 可以考虑增量,\(c(x,y,z)\) 相较于 \(c(x,y,z-1)\) 的变化是 \(rk(x,z)\) 和前 \(y\) 块形成的顺序对数量,所以 \(c(x,y,z)=c(x,y,z-1)+pre(y,rk(x,z))\)。然后就是 \(sum\),同样考虑增量法,\(sum(x,l,r)\)\(sum(x,l,r-1)\) 相比增加了一个数 \(rk(x,r)\),而这个数产生的顺序对数量就是在它前面的并且在 \((l,r)\) 之中的数量,于是有 \(sum(x,l,r)=sum(x,l,r-1)+p(pl(rk(x,r)),r)-p(pl(rk(x,r)),l-1)\)\(pl(rk(x,r))\) 就是块排名为 \(r\) 的数的位置。最后是如何回答问题的部分。

散块内部应该直接暴力二维数点即可(\(p\) 数组似乎本质上就是一个二维前缀和)。散块之间用一次归并就可以了。整块之间用 \(sum\) 就可以了。然后是整块和整块,枚举每一块然后用 \(c\) 求出大概的值,然后考虑不合法的逆序对实际上是当前块的数在 \([l,r]\) 中但是前面的不在,可以直接用数量相乘求出。最后是散块对整块,如果没有问题的话似乎也是可以用 \(pre\) 数组二维数点来解决。至此复杂度是 \(O(N\sqrt N)\),据说要卡常。

P5445

维护一个 \(01\) 序列,两种操作。一种是单点取反,一种是询问目前为止有多少个版本满足 \([x,y]\) 都是 \(1\)。然后考虑把询问 \((x,y)\) 看成是平面上的一个点,然后那些合法的区间就是对角线上的那些正方形。一次断开操作假设分裂出来的区间是 \((l,r),(x,y)\),那么 \(L\in[l,r],R\in [x,y]\) 的询问都不再合法了;反之就是变得合法了。然后为了维护这一过程,我们在连接的时候区间减时间,断开的时候区间加时间,这样的差值就是一个点在这一段中拿到的贡献了。至于如何快速找到某个点所在连通块的左右端点,直接用线段树维护即可,毕竟这玩意其实就相当于是区间覆盖单点查询,没啥。最后用 cdq 分治和二维数点即可,复杂度两只 log。

P3688

容易发现问题变成了在 \([l,r]\) 中修改一个数,然后多次查询 \(a_{l-1}\)\(a_r\) 是否相同。会发现维护 \(p_x\) 代表某一位置当前是 \(1\) 的概率是错误的,因为相当于默许了多个位置出现 \(1\);所以可以把询问 \((x,y)\) 看成是平面上的点,点值是两个点相同的概率。每次修改会对三个矩形产生影响,区间对应的主正方形中不变的概率是 \(1-2p\),其它有关系的矩形中不变的概率是 \(1-p\)。用 cdq 分治就可以了,然后思考如何用线段树来维护这种修改。发现每个区间维护两个 \(tag\) 就可以了,记为 \(tx,ty\),分别表示当前区间是否变化的概率,\(tx'=tx·p+ty·(1-p)\) 即可,复杂度两只 \(\log\)。但是由于不想写时间标记所以写的二维线段树。然后兴许要稍微卡一下空间,如果写 cdq 就没有这种烦恼啦。

SP687

枚举循环节的长度,考虑如何求在这个长度下的答案。发现一个循环串满足错开循环节长度之后可以匹配,所以可以选择位置 \(x,x+len\),然后查询两个前缀的最长公共后缀以及两个后缀的最长公共前缀,加起来再加上 \(len-1\) 就可以得到一个循环节的长度,除以 \(len\) 就可以得到子串重复次数。发现一个循环串只需要任意一个位置被遇到就可以被更新,而其长度最少是 \(len\),所以可以均匀取间隔为 \(len\) 的点尝试更新,求公共前后缀部分直接用后缀数组维护,总的复杂度是 \(O(TN\log N)\)

P8512

离线,然后考虑固定一个右端点之后每个左端点的答案,转化成思考每个左端点对应的修改会有多少留存的影响。考虑右端点挪动过程中的增量,显然维护一个 set,存储了整个序列被分割成了哪些颜色,然后尝试着用当前区间进行覆盖即可。最后用树状数组维护一个单点加后缀查,总复杂度 \(O(N\log N)\)

P6617

考虑每个点向前面的第一个匹配的点连边,记为 \(pre_x\),询问就变成了询问 \([\max\limits_{l\le i\le r}pre_i\ge l]\),用线段树维护即可。至于修改,唯一的问题是一个点可能是多个点的匹配点,这样一来修改的复杂度可能变得很高,所以我们希望把匹配关系变得唯一确定。然后有一个性质是说对于匹配的 \(x,y,y\),我们可以钦定第二个 \(y\)\(pre\)\(0\),因为它显然是可有可无的,于是关系就唯一了。最后用 set 随便维护一下即可,复杂度线性对数。

P4093

感觉上是一个非常显然的 cdq 分治。用 \(a_x\) 代表它原来的值,\(b_x\) 代表所有可能的情况下最小的值,\(c_x\) 代表最大的,那么点 \(x\) 可以更新 \(y\) 的答案当且仅当 \(x<y,a_x\le b_y,c_x\le a_y\),典型的三维偏序,随便做一下即可,理论上是 \(O(N\log^3N)\) 但应该可以跑得非常快。

P7880

大家都会这道题就我不会自闭力。

一个框架就是把贡献拆成一些点对,形如 \((x,y,v)\),表示如果这个区间同时包含了 \(x,y\) 那么会获得贡献 \(v\) 的机会,注意根据题目设定相同的 \(v\) 只会被贡献一次。根据某项链的方法可以考虑枚举右端点 \(y\),然后对于每个 \(v\),找到其对应的最靠后的合法贡献点 \(x\),把这个值赋值给 \(x\),查询就是一个区间和,树状数组维护即可,令点对数量是 \(T\),那么复杂度就是 \(O(T\log N)\)

于是问题就变成了如何使 \(T\) 尽量小。显然对于每个点 \(x\),它对应的 deep 要被贡献当且仅当有分别属于两棵子树的点同时被选中。枚举其中一个点,那么另一个点显然在不属于自己当前树的树内;而两个本质相同的贡献点 \((x,y),(x,z)(y<z)\),显然前者更优,也就是说确定了一个端点之后只需要在点集中找编号最接近的点产生贡献点对即可,用 set 维护。

此时需要使用一个称为 dsu on tree 的 trick,大概流程就是对于每个根选择重子树去掉,先对轻儿子进行处理,每次处理完之后清空当前集合;最后考虑当前根的贡献,方法是先处理重儿子,使得集合中保有其初始元素,然后枚举轻儿子中的点并在处理完一棵树后加入节点到集合中。这样出来的贡献次数是 \(O(N\log N)\) 级别的,可以接受;算上数据结构的复杂度应该是两只 \(\log\)

P9133

THUPC 五个小时切了一个题,傻逼的我应该立刻退役。

其实这道题的重点就是在于如何使用更加优雅的方式表示出来每个人得到的贡献。接下来来分析一下。

首先是购买点 \(x\) 所需要的费用 \(w_x\),这显然是逃不掉的。然后后面两个人交换游戏币的过程可以看成是购买这个点 \(x\) 时,假设子树 \(x\) 中对方的节点数是 \(A\)\(x\) 到根的路径上对方的节点数是 \(B\),那么你会先给对方 \(B\) 个游戏币,然后再从对方那里拿 \(A\) 个。把两个过程看成的独立的,并把给对方 \(B\) 个游戏币的操作延后考虑,这样可以得到一个重要的结论:当两个人都已经选完点了,那么光考虑对方给自己的游戏币数量,发现这个数量等于自己所有点的字数内对方点的数量之和。当然了,对方也可以通过这个东西来计算你需要给他的游戏币数量。

再次转化,考虑把每个点的贡献分开计算,那么它的贡献显然就是最后子树内的对方点数量减去祖先中对方点的数量,然而似乎还是没那么好计算。思考假设当前只有它一个己方点,其它都是对方点,此时它的贡献应该是 \(\text{size}-\text{deep}\);假设又加入了一个点,思考新点对于它的影响。如果它们不存在祖先关系那么显然不会产生影响,如果一个是另一个的祖先,那么会让祖先“子树内的对方点”减少一个,同时祖先也会让孩子点“祖先中的对方点”减少一个,一加一减下来会发现贡献的变化量刚好抵消掉了。也就是说一个点的贡献可以看成是不变的 \(\text{size}-\text{deep}\)

然后问题就显得比较明晰了,大概就变成了每个点有一个贡献 \(-w_x+\text{size}_x-\text{deep}_x\) 两个人轮流贪心选择。排个序就可以了,复杂度 \(O(N\log N)\)

P5369

怎么评价这道题呢,显然应该是不好评价。还是练习到了一些东西的,比如贡献点的设置以及如何保证贡献点合法等。由于数据范围很小考虑状压 DP,而最大前缀和这种东西显然是可以贪心的,我们选择了一个前缀 \(x\) 肯定是因为它不存在一个严格小于 \(0\) 的真后缀,并且后缀 \(x+1\) 一定不存在一个不小于 \(0\) 的前缀,注意二者是否严格,显然这会影响到转移点的合法性。然后问题就比较简单了,用 \(f_x\) 代表集合 \(x\) 满足前者的方案数,\(g_x\) 表示后者,转移上就是枚举在前面或者后面加上某个数之后是否仍然合法,注意它存在最大前缀和为负数的情况即可。复杂度大概是 \(O(2^nn)\)

P6189

其实就是一个根分的 trick。由于背包的特性,可以把物品分成两类,一类是价值不超过根号的,一类是价值超过根号的,考虑分别计算最后合并。前者由于数量只有根号个可以做暴力的完全背包,复杂度 \(O(n\sqrt n)\)。而后者具有选择的数量不超过根号的性质,所以可以使用另外一种 DP 的方法。此时有一种转化的方法,考虑把每个物品拆成某个数量个块,然后把这些块整齐地摆成一排,然后竖着看,这样似乎就变成了物品上限为根号的完全背包。但是我们要求只使用价值超过根号的物品,挪动到新的模型上来说限制就是说最少的那个物品至少要选择 \(t\) 个。所以在做完全背包的时候需要特殊处理一下,然后就没什么了,复杂度和前面是一模一样的。

P5527

又学到一个思想。首先题目中那个什么两个集合不相交的限制可以忽略,因为两个相交的集合去掉他们的交就是一组合法的解。于是长度为 \(n\) 的子序列有大概 \(2^n\) 个集合,而由于值域很小,总的本质不同的集合数量不会超过 \(nV\),根据鸽巢原理,\(n\ge 14\) 的时候一定有两个集合和是相同的。于是问题就比较简单了,日渐变成 bitset 的形状了。就考虑 \(n\) 比较大的时候一定合法,比较小的时候暴力使用 bitset 做背包,然后枚举每个物品钦定它在其中一个答案集合中,那么合法当且仅当不使用这个物品仍然能凑出 \(x,x+v\),左移取与即可。复杂度 \(O(q\frac{t^2V}{\omega})\)

P8360

当时做第二分块的时候其实没有完全理解其精髓,这次做到类似的题可以总结一下。数据范围和时间限制告诉我们要分块,每个块内的操作其实也比较简单,就是颜色赋值和颜色加。颜色加比较简单,就单纯统计每个颜色的数量即可。颜色合并比较麻烦,因为旧颜色对应的位置集合本身每个点都带有一个 lazy,会发现如果整体操作的话会陷入下放标记也不行合并标记也不行的糟糕情况。此时就需要请出势能分析这尊神,并对其进行分类讨论。如果目标颜色不存在,那么只需要把并查集的根节点颜色改一下即可;如果目标颜色存在,那么块内的颜色数量会少一个,所以这样的情况只会出现根号次,所以暴力重构复杂度是正确的。实现上为了赋值颜色的方便,维护的实际上是一个位置集合。然后就是写分块的时候要注意的一点细节,由于只有我这种傻逼才会犯这样的错误所以就不说了吧。整体复杂度是 \(O(n\sqrt n)\)

P7519

感觉上是一道比较简单的题。一眼状压,而题目中 \(b_x\) 递增的限制想到费用提前计算,而费用提前计算有一个好处就是某个未涉及的后缀中任意两个元素的相对差值是不会改变的,于是用 \(f_{x,y,t}\) 代表集合 \(x\) 已经滚出来了,最后一个出来的队伍是 \(y\),并且已经用了 \(t\) 道题的前提下有多少合法的排列。考虑转移,显然枚举下一个队伍 \(z\),而考虑下一次贡献的题目 \(t'\) 要合法当且仅当 \((t'-t)/n+a_z\ge a_y\),也就是说 \(t'\ge n(a_y-a_z)+t\),然后需要注意两个点队伍的编号关系决定了等号是否合法。除此之外由于贡献的提前计算,\(t'=t+kn\),所以在遍历到一个状态 \(x\) 时要先通过差分数组还原出真正的 DP 数组。这是容易的,复杂度 \(O(2^nn^2m)\),感觉要卡常呢。

实际上这道题要稍微复杂一些。由于题目中要求的是寻找合法的排列数量,所以呢如果按照上面的法子会导致重复统计。而题目中有一个性质是说一个排列假如使用 \(a\) 个题目可以完成那么更多的题目也一定可以,因为最后一个队伍获得的题目是多多益善的。所以应该用 \(f_{x,y,t}\) 表示已经滚了集合 \(x\) 上一个队伍是 \(y\) 并且最少使用的题目恰好为 \(t\) 道的排列数量,这样就可以正确转移了。然后还有些细节。

P8292

至少根分的解法应该是比较好想的。考虑正难则反,如果质数数量比较少可以暴力容斥,具体而言对于给出的质数集合 \(P\) 的一个子集 \(x\),令与这个集合交集为空的集合数为 \(t\),那么这里的方案应该就是 \(2^t\),对应地乘上容斥系数累加即可得到答案。

但是事实上质数集合是很多的,于是想到使用经典的寿司晚宴一样的套路对质数大小进行根分。由于 \(43\times 47>2000\),所以任何一个数与大质数(不小于 \(43\) 的集合)的交集大小应该不超过一,所以可以根据此对所有数进行分类,也就是说任何一个数不可能同时包含多个大质数,于是就没有容斥的必要了。于是在容斥集合 \(x\) 的时候我们强制它包含所有大质数,于是需要做的就是遍历给定的大质数集合,求出其与 \(x\) 交集为空的数目 \(t\),这一类的答案显然就是 \(2^t-1\)。其它数也是好统计的。然后似乎就没了,听说也可以用 FMT 等方法但是不清楚,而且听说会被卡常。大雾。我的写法下需要注意 \(43\times 43\) 这个数因为它需要继续分解。需要注意的是整体应该是大质数都存在的整体方案,否则会挂成 \(25\) 分。

P5303

很板的题目。用 \(f_x\) 表示前 \(x\) 列的答案,考虑最后两列的状态。如果是正常放的话方案数是 \(f_{x-1}+f_{x-2}\),如果不正常的话相当于在前面寻找一个长度不小于三的长条,而长条之前的方案数是很好算的,即 \(\sum\limits_{i=0}^{x-3}F_i\),记 \(F\) 的前缀和为 \(g\),而根据结论有 \(g_x=F_{x+2}-1\),所以整个的递推式应该就是 \(f_x=f_{x-1}+f_{x-2}+2F_{x-1}-2\),矩阵快速幂优化即可。还是列出来吧。

\[\begin{bmatrix} f_x\\f_{x-1}\\F_{x}\\F_{x-1}\\-2 \end{bmatrix} =\begin{bmatrix} 1&1&2&0&1\\ 1&0&0&0&0\\ 0&0&1&1&0\\ 0&0&1&0&0\\ 0&0&0&0&1 \end{bmatrix} \times \begin{bmatrix} f_{x-1}\\f_{x-2}\\F_{x-1}\\F_{x-2}\\-2 \end{bmatrix} \]

P8353

知名傻逼题目,很好奇出题人的心理状态。从某种意义上来说加深了我对虚树的理解。

首先随机 \(\sqrt n\) 个点建立虚树,于是树上的节点就分成了三类,一类是虚树上的节点,一类是虚树边上的点,一类是其它和虚树没有任何关系的点。所以无论什么操作都应该先向上跳到第一二类节点再进行操作,只要理解了这些内容其它的都还好,细节其实也没有想象的那么多,但还是有,比如修改一段链的时候要注意修改下面那段的和。

P6018 & P6623

就是那个全局加一的 01 trie的 trick。大概的流程就是从根开始遍历,然后尝试交换左右两个孩子,会发现这一位上异或值的变化量就相当于是这个点的 size 与 1 取交的值乘上权值,因为其中所有数都异或上了这个值。然后就没什么了,P6623 是这道题加上一个 01 trie 合并,不知道复杂度是不是正确的但确实能过。无所谓了。

P7521

也是一个比较常见的 trick。对于一个数 p,把所有数模上 p 之后排序,那么可以大概把可能的贡献分成两类,一类是和大于等于 p 的,一类是小于的。对于前者显然贪心地取前两大即可,对于后者可以双指针随便维护一下。回到这道题,考虑从大到小枚举 p,如果发现当前 p 小于已经得到的 ans 就直接退出,可以证明复杂度是正确的。

P8330

一道很好的根号分治题目。题意可以转化成把原序列中分离一个子串出来,答案就是子串中的众数个数加上字串外的众数个数。考虑对中间那个子串的出现次数进行根号分治,分成两类。

一类是其出现次数很多,假设中间的数是 \(x\),外面的数是 \(y\),可以直接枚举每个 \(y\) 考虑更新答案。如果中间的串长度为 \(0\),那么答案应该是 \(num_y\);选择的串中每个 \(x\) 显然会贡献一个答案,每个 \(y\) 会减少一个答案,所以把序列中每个 \(x\) 赋值为 \(1\),每个 \(y\) 赋值为 \(-1\),求最大子段和。有贪心的想法是答案子串一定在两个 \(-1\) 之间,所以可以用 \(num_y\) 的时间求出每个 \(y\) 的答案,所以这一部分的时间是线性根号的。

一类是出现次数比较少,可以枚举其出现次数。对于一个次数,假设当前扫描到的右端点是 \(R\),可以求出最大的 \(L\) 使得区间 \([L+1,R-1]\) 中存在一个数出现次数达到标准,可以对于每个位置维护前面某个数的位置求一个前缀最大值即可。于是可以还是枚举每个外面的颜色 \(y\),然后用双指针维护一下每个右端点对应的最好的左端点位置即可,这部分也是线性根号的。于是就解决了这个问题。

P5038

黑白染色。格子数为偶数的时候答案显然具有单调性,考虑二分。白点连源点,黑点连汇点,流量就是二分出来的值减去初始值。然后相邻的黑白点之间连边,然后尝试跑最大流即可,如果可以流完显然合法。如果是奇数的话发现偶数点天然多一个点,解个方程就能得到最后的唯一可能的答案,检查一下即可。

P6640

BJ 的加试题,感觉很魔怔。考虑对于 \(s\) 中的每个前缀求出它的最长的是 \(t\) 子串的后缀,这里可以用 SAM 方便去做。具体就是尝试找出边,如果没有就一直跳父亲。于是问题就转化成了有一些区间,然后找给定区间和这些区间中交最大的最大值。我写的无脑写法,从左往右扫描,显然有些区间的价值会加一,有些不会变,并且这些变化的次数是线性的,所以开两个线段树就可以了。复杂度线性对数。

P5342

一道有意义的题目,虽然和 CF750G 重了(UKE 今天表示 TJOI 一定会臭名昭著)。首先考虑一个点到根的路径上的点权之和,会发现对于点 \(x\) 的每一位进行考虑,那么在整个过程中这一位 \(1\) 会在一个后缀中产生贡献,每一位都会被拉伸成全 \(1\) 的形式,所以 \(x\) 到根的路径权值应该是 \(2x-cnt(x)\)。考虑如何使用这一点。第一问是简单的,思考如何解决第二问。和是好算的,那么贡献应该有三种形式。一种是单点,判一下就可以了。一种是一个点是另一个点的祖先,假设这个祖先是 \(t\),并且枚举二者的深度差 \(h\),会发现一件非常有趣的事情。首先路径上每个点都会有子段 \(t\),所以首先就有一个贡献值为 \(t(2^{h+1}-1)\)。然后思考除此之外的后缀 \(1\) 的影响,如前文所述,从低到高的第 \(i\) 位的 \(1\) 会有 \(2^i-1\) 的贡献,所以呢我们希望的就是 \(t(2^{h+1}-1)+\sum\limits_{i=1}^hp_i(2^i-1)=w\)。然后发现后面那一堆不会达到 \(2^h-1\) 的大小,所以相当于是 \(t\) 的值已经固定下来了,只需要思考是否存在合法的 \(p\),贪心即可。

最后就是存在分叉的情况,假设 LCA 是 \(t\),那么两个孩子应该是 \(2t,2t+1\) 的孩子。还是按照前面的方法,枚举两个孩子和 LCA 左右孩子的深度差 \(h,s\),那么总的贡献应该是 \(2t(2^{h+1}-1)+(2t+1)(2^{s+1}-1)+t\),后续相应地也有 \(\sum\limits_{i=1}^hp_i(2^i-1)+\sum\limits_{i=1}^sq_i(2^i-1)\) 的贡献,还是影响不到前面的,所以 \(t\) 的值还是固定的。但是呢后者无法用贪心来解决,所以只能使用数位 DP 了,套用经典的做法,考虑到那个 \(-1\) 很烦,于是枚举选择的数的个数,然后用 \(f_{x,y,0/1}\) 表示已经选了最低的 \(x\) 位,并且已经用了 \(y\) 个数,这 \(x\) 位有没有向上进位的方案,随便做就可以了。总复杂度发现是 \(O(Tn^5)\),常数大概也许会比较小。

P6134

一道比较神秘的题目。手玩发现一条边可以被断掉当且仅当这条边可以被其它一系列边所取代,也就是说 \(u\rightarrow v\) 存在多条路径,似乎并不是很好做。考虑用 bitset 来维护,为了做多条路径可以维护两个 bitset,令为 \(f,g\),每加入一条边思考哪些点会存在多条路径,显然是点 \(x\),不经过这条边也可以到达 \(y\),所以与一下就可以了,复杂度 \(O(\frac{mn}{\omega})\)

P3287

为什么记得八百万年前就见过这个题,但是一直没做。

显然的结论是拔高一个后缀一定不劣,所以用 \(f_{x,y}\) 表示前 \(x\) 棵玉米拔高 \(y\) 次之后的答案,考虑它的转移点 \(f_{a,b}\) 需要满足的条件。显然要满足 \(b\le y,h_a+b\le h_x+y\),二维树状数组维护一下即可。话说我感觉我还从来没写过二维树状数组。

P5319

感觉上非常披风的题,但多少是有点价值的。考虑贡献的柿子:

\[\text{ans}=\sqrt[c]{\prod\limits_{i=1}^c}a_i\\ \ln\text{ans}=\dfrac{1}{c}\ln\prod\limits_{i=1}^ca_i=\dfrac{1}{c}\sum\limits\ln a_i \]

显然最大化后者即可,而后面是个平均数的形式,考虑分数规划。二分出来一个值后令 \(a'_i=a_i-v\),然后在 AC 自动机上跑最优答案即可。

然后这道题还暴露出我的一些问题,比如 ins 的时候末节点应该设置为遍历到的节点,以及 fail 树这个概念的存在,到达一个点意味着到达这个点到根的所有点,所以贡献要累计。

P5904

上一次尝试写这个题的时候是 2022 年的一月,转眼间已经过了十五个月了。现在想来,感慨万千,那些个奇怪的遭遇如梦一般,不胜唏嘘。

三个点显然会有一个 LCA,在 LCA 处统计即可。用 \(f_{x,y}\) 表示子树中和 \(x\) 距离为 \(y\) 的点数,\(g_{x,y}\) 表示如果存在一个点距离点 \(x\) 的距离为 \(y\) 的话就能构成三元组的点对数量,考虑转移。首先统计答案,显然已经处理过的孩子和当前孩子构成两个集合,考虑新集合中有几个点。如果有一个点,那么答案应该就是 \(g_{x,t}\times f_{y,t-1}\);如果有两个,那么答案应该是 \(g_{y,t}\times f_{x,t-1}\),其实本质是相同的,都是以 \(x,y\) 中间的这条边为基础在做文章。然后思考 \(f\) 的情况,显然只需要长剖就可以解决。最后思考 \(g\) 的转移,还是看新集合中有几个点。如果只有一个,那么答案应该就是 \(f_{x,t}\times f_{y,t-1}\);如果是两个,那么答案应该就是 \(g_{y,t-1}\)。也是可以用长剖来解决的。

然后是一点细节上的问题。长剖继承的时候 \(g_{y,t}\) 更新到的其实是 \(g_{x,t-1}\),所以呢分配空间的时候是向前分配的;既然是向前分配的,开数组的时候就要留足前后各 dep 的空间。

P3899

没什么意思的题。考虑给定的点 \(p\) 的位置,如果是夹在两个点之间,那么答案显然就是 \(\min(k,dep_p-1)\times(size_p-1)\);如果两个点都在下面,思考一个点 \(q\) 的贡献,发现其贡献其实就是子树大小减一。于是深度和 dfn 序表示一个点的贡献,二维数点即可,复杂度线性对数,跑得飞快。直觉上觉得没有人会去写长剖。

P3768

欧拉反演的方法。

\[\begin{aligned} &\sum\limits_{i=1}^n\sum\limits_{j=1}^nij\gcd(i,j)\\ =&\sum\limits_{i=1}^n\sum\limits_{j=1}^nij\sum\limits_{t|i,t|j}\varphi(t)\\ =&\sum\limits\limits_{t=1}^n\varphi(t)t^2\sum\limits_{i=1}^{n'}\sum\limits_{j=1}^{n'}ij\\ =&\sum\limits_{t=1}^n\varphi(t)t^2(n'\times(n'+1)/2)^2 \end{aligned} \]

用杜教筛即可。需要注意取模的过程,然后似乎要卡一点常。

P1447

\((x,y)\) 的答案显然是 \(2\gcd(x,y)-1\),把柿子列出来。

\[\begin{aligned} \text{ans}=&\sum\limits_{x=1}^m\sum\limits_{y=1}^n2\gcd(x,y)-1\\ =&-mn+2\sum\limits_{x=1}^m\sum\limits_{y=1}^n \gcd(x,y)\\ =&\dots\sum\limits_{x=1}^m\sum\limits_{y=1}^n\sum\limits_{t|x,t|y}\varphi(t)\\ =&\dots\sum\limits_{t=1}^{\min(m,n)}\varphi(t)n'm' \end{aligned} \]

甚至不需要进行数论分块。暴力去做就可以了,复杂度线性。

记得 \(\mu\)\(\varphi\) 的边界都是 \(1\)

P3704

\[\begin{aligned} &\prod\limits_{i=1}^n\prod\limits_{j=1}^mf_{\gcd(i,j)}\\ =&\prod\limits_{g=1}^{\min(m,n)}\prod\limits_{i=1}^{m'}\prod\limits_{j=1}^{n'}f_g^{\varepsilon(\gcd(i,j))}\\ =&\prod\limits_{g=1}^{\min}f_g^{\sum \varepsilon(\gcd)} \end{aligned} \]

上面那一堆东西可以得到:

\[\sum\limits_{i=1}^{m'}\sum\limits_{j=1}^{n'}\varepsilon(\gcd(i,j)) =\sum\limits_{t=1}\mu(t)\frac{m}{dt}\frac{n}{dt} \]

\(dt=T\),提取出来可以得到:

\[\prod\limits_{T=1}\prod\limits_{g|T}f_g^{m'n'\mu(T/g)}=\prod\limits_{T=1}(\prod\limits_{g|T}f_g^{\mu(T/g)})^{m'n'} \]

后面那个东西只和 \(T\) 有关,暴力预处理即可;剩下的部分数论分块即可,相当于求一个区间积,用差分处理即可做到 \(O(1)\) 询问。于是总的复杂度是线性根号加上一个值域对数的预处理。

P4449

尝试大力莫反。

\[\begin{aligned} &\sum\limits_{i=1}^n\sum\limits_{j=1}^m\gcd(i,j)^k\\ =&\sum\limits_{g=1}\sum\limits_{i=1}^{n'}\sum\limits_{j=1}^{m'}g^k\varepsilon(\gcd(i,j))\\ =&\sum\limits_{g=1}\sum\limits_{i=1}^{n'}\sum\limits_{j=1}^{m'}g^k\sum\limits_{t|i,t|j}\mu(t)\\ =&\sum\limits_{t=1}\sum\limits_{g=1}\sum\limits_{i=1}^{n'}\sum\limits_{j=1}^{m'}\mu(t)g^k\\ =&\sum\limits_{t=1}\mu(t)\sum\limits_{g=1}g^kn''m''\\ =&\sum\limits_{T=1}n''m''\sum\limits_{t|T}t^k\mu(T/t) \end{aligned} \]

前面显然可以整除分块,考虑后面的部分如何解决。即考虑 \(g(x)=\sum\limits_{t|x}t^k\mu(x/t)\) 的性质。

由于存在多个质因子的 \(\mu\) 是无效的,所以从递推的角度上来说,如果新的质数已经出现过那么项数不变,只是所有项的第一项会乘上一个 \(p^k\)。如果这个质数没有出现过,那么项数会是原来的两倍,质数在 \(\mu\) 这一边的会导致答案变成原来的相反数(\(\mu(A\times P)=-\mu(A)\));而剩下的部分参照前面会乘上一个 \(p^k\),所以贡献就是 \(p^k-1\)。然后就可以线性递推啦。

P5290

很有意思的题目。

首先考虑根只有两个链做儿子的情况,显然每次最多只能选择两个数。然后思考有没有比较特殊的过程,其实是有的,那就是最大的那个孩子,假设在左子树。那么发现选择这个孩子的时候代价已经固定了,无法再进行缩减了,所以贪心地选择另一个子树中最大的孩子,因为这样可以尽可能地消耗掉对方的有生力量。然后把两个孩子删掉就得到了一个新的问题,策略与之前保持一致即可,用两个堆维护一下。

尝试扩展到一般情况。假如一个点下面挂了很多条链,那么每次都是把每个孩子的最大值拿出来,消耗其中最大值的代价消掉所有的点。重复这一过程。再次进行扩展,考虑一个点的点权为这个过程中消耗的点的集合,然后这个过程就变成了在孩子中依次选最大的,用堆和启发式合并即可做到两只 \(\log\)

P5458

思考题目中限制的本质。考虑进行红黑绿染色,那么不满足限制当且仅当一个黑点周围既有绿色又有红色。于是考虑绿点向黑点连边,黑点向红点连边,跑一个最小割即可。然后有一些坑点,比如坐标的表示法,比如一个位置上可能存在多个宝石等。

P1791

就是一个网络流的板子题。还是先把能加的贡献都加上,在这道题中就是同事之间的默契度;然后每个同事向汇点连其工资的边,代表如果他和源点相连就需要扣除这么多钱。然后每个同事向源点连他所有好感度之和的边,表示如果他倒向敌方了会少得到那么多价值。最后对应的同事之间连两倍价值的边,表示如果两个人发生了冲突会导致比原来减少两倍的价值,在这个图上跑最小割即可。反正就是记住,最小割的宗旨就是把所有可能的贡献先加上再来考虑哪些贡献可能会消失,并最小化消失的值。

P3462

一个挺好的贪心题,然后有一种挺好的思想。由于任意一对物品之间都是倍数关系,所以最多只有 \(\log\) 种物品。然后倍数关系还带来一个内容就是说我们可以把每个背包当成是一个广义的某进制数,然后这个进制是可加的,所以把所有数的进制加起来最后贪心是正确的。比较强。

P4363

没有什么意思的题目。显然最后的状态数比较少,具体地每个状态都可以用 \(n\) 个竖线和 \(m\) 个横线来表示,所以状态数是 \(2^{m+n}\) 级别的。至于博弈的部分,只能从后向前进行转移,每次从后续状态中选择最优的转移即可。

P7215

很神仙的一道题。首先题意是说选择一些颜色,让这些颜色在树上形成一个连通块,求最小的颜色数量。现在才发现其实自己理解错题意了。

考虑暴力的做法。以每个点为根考虑答案,大概就是找出所有和这个点颜色相同的点,然后尝试让这些点向上跳,然后把所有颜色并起来就是答案。在所有点的答案中取最小值即可。

此时发现题目的一个性质,就是我们可以点分治,并且采用一样的方法。如果这个过程中出现了当前分区以外的点,那么这次计算是无效的,因为那样的话颜色会跨过某一级的祖先,也就是说一定是要选择这个祖先所需要的所有点,肯定是不优的,于是直接跳过即可。

P2056

很有一段时间没有写过点分树了,比较悲惨。显然点分治,然后对于每个分治中心维护所有孩子中最远点离自己距离的最大值和次大值,正常更新即可。考虑到过程中会需要删除,所以需要使用可删堆来维护。

P4074

树上带修莫队的板子。我连树上莫队的板子都忘了怎么写了,大废物一个。

使用的欧拉序,如果两个点的欧拉序在树上是相离的,那么只需要调用前者的第二个标记以及后者的第一个标记即可,然而这样是不包括 LCA 的,需要额外计算。如果是包含的,也就是其中一个点是另一个点的祖先的情况,只需要调用两个点的第一个标记即可,无需额外计算。带修很容易。

P4886

也是一个挺好的点分治题目。

对于一个分治中心,尝试计算所有点对的答案。显然会有一个最大值,如果最大值跨子树的话显然答案不能更小了,因为移动了要么无法减小它,要么会使得这个值变大,横竖都不优;相似地,如果存在两组值分别在两棵子树中,那么也无法通过移动决策点来使得答案更优,所以唯一可能移动决策点的情况是存在唯一的最大值,或者说唯一的一棵子树中有最大值,往这个子树中移动即可。整个过程中可能遍历到的决策点显然是 \(\log\) 级别的,所以整个的复杂度是线性对数。

P8339

究极 zyc 题目,虽然其实题目质量挺高的,但还是很难受。

给每种颜色建虚树,然后思考会产生什么样的贡献。显然贡献只和第一个到达的钥匙以及它的延申方向有关,并且任意一条路径都会形成一个类似于括号嵌套的关系。思考每个钥匙可能和哪些锁匹配,显然这个括号序列的情况和之前遇到了哪些并不发生关系,所以考虑从钥匙开始向下遍历,每条路径找到第一个合法的锁,显然如果一条路径同时包含了这个点和这把锁,那么就能产生一个贡献。并且略加思考发现一把钥匙在一轮游戏中只可能贡献一次。对每把钥匙都这么做一次,由于题目中不可抗因素影响了钥匙的数量,这样做的复杂度是正确的。上述限制可以通过小的分类讨论转成起点和终点的 dfn 范围,扫描线做一次就可以了。树剖求祖先的对应孩子时一定要考虑自己就是答案的情况。

P5048

在线区间众数,要求线性空间。

还是按照不卡空间的方法,先求出整块的区间众数,这个可以暴力去做。然后考虑散块中的数,用 vector 维护每个颜色的位置序列,然后对于每个散块中的颜色尝试去扩展答案,一直扩展到答案对应的位置超出询问。显然答案最多只会扩展根号次,所以总的复杂度是正确的。

posted @ 2023-03-26 16:59  Feynn  阅读(49)  评论(0编辑  收藏  举报