Cry_For_theMoon  

1. Niyaz and Small Degrees

这个题做完才发现是 APIO2021T3 的原题,感觉当时被这套题干爆的时候还近在眼前。

考虑确定 \(x\) 的话有一个比较显然的 dp + 排序后贪心的 \(O(n\log n)\) 解法。

我们考虑如果一条边 \((u,v)\),在 \(x\ge \max\{deg_u,deg_v\}\) 之后就不会删了;如果 \(x\le \min\{deg_u,deg_v\}\) 那我们保留这条边;这样 \(n\) 个时刻的连通块大小显然还是 \(O(n)\) 的,问题就在于介于 \(\min,\max\) 之间,这个时候是一个存在的点挂了一个限制已经无所谓的叶子。

考虑对连通块的每个点做树形 dp,用数据结构维护每个点挂着的额外的叶子(按照 weight 排序),然后合并的时候我们需要一个和额外挂着的叶子的数量无关的做法。

考虑实际上是把两个序列归并起来:一个序列是额外的叶子,一个序列是所有的儿子。

第二个序列的大小是可以接受的,所以我们在他上面二分出到底要选多少个,这样的话我们的数据结构只需要支持 k-th prefix sum 还有 \(\le x\) 的值的个数两个东西,用平衡树去维护即可。

这样时间复杂度是 \(O(n\log^2 n)\) 的。

记录

2. Puzzle Lover

这个题初看不难,但其实细节讨论很多,非常考验实现(当然也能写的很短)。

对于 \(k\le 2\) 的需要特判,因为他们可以只在一列间游走。

如果只能走左右上三个方向那是比较容易 dp 的,但多了一个方向就比较困难。

发现走回头路一定在开始和中间:换言之假设起点在终点的左边(同一列最后单独算)则就是起点向左再向右,然后中间段是右上下三个方向,到终点所在列的时候进入第三阶段:向右再向左。

第二个过程用 dp 算,第一个过程暴力枚举两个边界作为初始值,然后把第二过程的值用到第三过程的枚举上。最后反过来再做一次,时间复杂度 \(O(n^2)\),判的时候要用哈希。

好像还卡哈希模数,素质真低。

记录

3. Number of Binominal Coefficients

根据 kummer 定理,组合数 \(\dbinom{n+m}{m}\)\(p^a\) 的幂次,等价于 \(n,m\)\(p\) 进制下做加法的进位次数。

因此问题变成了求 \((x,y)\) 满足 \(x+y\le A\) 且两人在 \(p\) 进制下加法进位次数 \(\ge \alpha\) 的对数。

\(A\) 转成 \(p\) 进制后暴力数位 dp,时间复杂度 \(O(len^2)\)

记录

4. Max of Medians

考虑二分中位数 \(mid\) 然后问题变成求最多能配出多少对 \(\ge mid\) 的数。

考虑在 trie 上贪心匹配。

定义 \(f(x)\) 表示在 \(x\) 的子树内的答案;如果 \(mid\) 的当前位为 \(0\) 则我们一定是两个儿子间优先匹配,然后数量多的那个儿子递归下去求 \(f\);如果 \(mid\) 的当前位为 \(1\) 则只有那些一个是左儿子一个是右儿子的点才可能计入答案,因此考虑设一个 \(g(x,y)\) 表示一个人要取 \(x\) 子树内的点,另一个人要取 \(y\) 子树内的点;这样 \(f\) 可以利用 \(g\) 转移;来考虑 \(g\) 的转移。

如果 \(mid\) 的当前位是 \(1\) 那么类似的:把 \(x\) 的左儿子和 \(y\) 的右儿子配对;\(x\) 的右儿子和 \(y\) 的左儿子配对,这样递归下去算两个 \(g\);否则所有一个是 \(0\) 一个是 \(1\) 的配对已经合法,但可能我们把 \(x,y\) 的左儿子都拿出来一个数配对也会合法(右儿子同理),所以剩下的那些继续下去配对。

由于每次遍历 trie 上的节点至多一次,所以复杂度为 \(O(n\log^2 V)\)

记录

5. Constrained Topological Sort

考虑将 \(L,R\) 的限制变严格使得:若 \(u\) 能走到 \(v\)\(L_u\lt L_v\)\(R_u\lt R_v\),这可以拓扑一次然后做到。

接下来忽略拓扑序限制,构造一组让每个人的 \([L,R]\) 都满足的解。这可以通过扫描线 + 贪心的方式实现。

若此时构造不出解,容易发现原问题一定无解;否则我们总能调整得到一组原问题的合法解。

考虑若一条边 \((u,v)\) 满足 \(pos_u\gt pos_v\),则我们交换两人位置,由于 \(L_u\lt L_v\)\(R_u\lt R_v\) 这个交换一定满足他们各自的区间限制;同时由于靠前的的点位置更往前,靠后的点位置更往右;我们只需要按拓扑序去调整每一对 \((u,v)\),就不会让先前的合法边变为不合法。

时间复杂度 \(O(n\log n+m)\)

记录

6. Kuroni and Antihype

\(v\) 是被 \(u\) 激活的则我们连边 \(u\rightarrow v\),这样我们会得到一个森林且答案为 \(\sum_{u}a_{fa_u}\),注意一个点的 \(a\) 可能被统计多次。

如果我们考虑对每条边 \((u,v)\)\(a_u+a_v\) 加起来的话,会发现除了根节点,每个点恰好被多算了一次。

如果建一个虚点 \(a_{n+1}=0\),认为这些自己激活的点是被 \(n+1\) 拉进来的,则答案就是 \(\sum_{(u,v)}a_{u}+a_v-\sum_{u}a_u\)

此时变成了一个最大生成树问题!

考虑 boruvka:我们需要对每个点 \(u\) 求出,此时和 \(u\) 不在一个连通块且 \(a\) 最大的点。

如果考虑比较常规的手法,把一个连通块的点先删除再求子集内最大值,则是不太好维护的。

我们考虑每一轮,对于每个 \(S\) 求出 \(S\)\(a\) 最大的点,只有这个信息是不够的因为可能和 \(u\) 在一个连通块,因此我们再维护第二个东西,表示和最大值不在一个连通块的所有点里的最大值;这样这个二元组信息是可合并的,也支持我们的询问。

时间复杂度 \(O(n+a\log^2)\)

记录

7. Nora's Toy Boxes

神中神。

这个 \(60\) 的数据范围看着就很离谱,所以来分析一下性质。

每次选的 \(a_i\) 会满足:不存在一个 \(a_i\) 的约数还存活,不然肯定不优。

注意到这样的 \(a_i\) 不会很多:经典结论是 \(n\) 的值域最多可以构造出 \(n/2\) 个这样的 \(a\),一看还是有 \(30\) 个,昏过去了;但是如果 \(a\gt 30\) 他就不可能作为 \(a_i\),这下只有 \(15\) 个了。

所以这 \(15\) 个是基本元,永远不动的。然后每个数有一个集合 \(S\),初始有集合 \(T=0\)

我们把删数的过程倒过来变成加数,一个数有两种可能:

  • 保留

  • 放进结果序列

如果一个数想放进结果序列,则 \(S\)\(T\) 交集非空,然后 \(T\) 会 or 上 \(S\);如果是保留,那么就没有交非空的约束,\(T\) 就直接 or 上 \(S\) 了。

然后因为我们只对结果序列计数,所以我们强制保留操作在放进结果序列的人之前全部出现且这部分无序。

考虑设 \(dp(mask)\)\(T=mask\) 的结果,那注意到如果我们加入一个数 \(S\in mask\) 就自己转自己了,所以我们额外记录已经用了多少个数;

但是注意到我们此时无法区分说这用了的数里有几个是保留的,有几个是放进结果序列的。这就不能做了。

\(a_i \mid a_j\) 则连边,对每个弱联通图,去除基本元后,我们需要且仅需要保留一个人,就能把剩下其他人放进结果序列(因为弱联通的性质),所以对每个弱联通块分别考虑最后组合数合并一下;那么我们知道不管已经用了多少个数,只有一个人是保留的,那就做完了。

时间复杂度 \(O(2^{\frac{v}{4}}n^2)\),逆天。

摸鱼酱说可以做到 \(O(2^{\frac{v}{6}}n)\),更恐怖了,这下 \(n,v=120\) 了。

记录

8. A Game on Strings

上来的感觉肯定是结论 + DS 维护之类的,但是这个题特殊在于他没有什么很好的结论。

考虑比较暴力的区间 dp 算 SG 值,注意到当我们拆掉一个字符的时候,除了首尾两端,剩下的段都满足 \(s_{l-1} = s_{r+1}\) 且这个字符在中间不再出现,这样的段数显然是 \(O(n)\) 的。

对于两边来说,我们算的段满足这个性质:他是某个 \(i\)\(i\) 右边第一个字符 \(c\) 的位置(不含),或者是到左边。这样的段数显然是 \(O(n\Sigma)\) 的。

更进一步地,发现上面第一种情况也在下面这种情况中。

我们考虑把这些区间按照长度排序然后每个区间 \(O(\Sigma)\) 暴力去算,但是这样涉及一个问题就是你要快速求中间那些段的 xor 和(询问的时候也需要,但是这个时候全问出来了肯定是很好求的),可以考虑用序列上的并查集去维护,这个技巧非常高妙啊。

这样时间复杂度是 \(O(n\Sigma^2 + m\Sigma)\),常数其实并不小(还有很大的 cachemiss)所以跑的不算快,注意到并查集是挂在 \(n\) 上面的(因为第一类段只有 \(O(n)\) 个)所以应该是不影响复杂度的。

记录

9.Animism

拜谢 larryzhong。

考虑点分:对于所有询问,我们强制他们经过重心。

则我们对每个点需要求:他到达重心 \(r\) 的时候剩的最多油 \(f_u\),以及从重新走到他所需要的最少油 \(g_u\)

\(f,g\) 求法是类似的,以 \(f\) 为例。

考虑对每个点的加油站,按照 \(c_u\)(容量)\(-dis(u,r)\) 排序,则最大的点他的答案肯定是确定的,然后考虑反向 bfs:所有能走到他的点答案和它保持一致并删除这些点;然后再次取出最大的点,重复这个过程即可。

那么问题在于我们每次怎么快速找到能走到他的点。

\(x\) 能走到 \(y\),考虑点分树上的 LCA \(p\),肯定是 \(c_x\ge dis(x,p)+dis(y,p)\),也就是 \(c_x-dis(x,p)\ge dis(y,p)\) 那么按照 \(c_x-dis(x,p)\) 对挂在点分树上的某个点排序(可以建点分树的时候就排好),然后能走到 \(y\) 的就是一段前缀,我们只需要找到这段前缀里没被访问过的值,可以用序列上的 dsu 来实现(注意一个点被取出后在 \(\log\) 个点分树祖先的 dsu 都要修改)。

这样可以在 \(O(V\log n)\) 的时间内完成计算,\(V\) 是当前的点数,根据点分治的过程总复杂度就是 \(O(n\log^2 n)\) 的,\(g\) 的计算同理。

然后考虑每条路径,还要问从一个点 \(u\) 出发不超过 \(s\) 的油量能到达的 \(f\) 最大的点是谁。

依旧在点分树上去做,对每个点 \(p\),把它子树里的点 \(u\) 也按照 \(dis(p,u)\) 排序然后算 \(f\) 的前缀 max 就好了,这部分是 \(O(q\log^2 n)\) 的。

所以总时间复杂度就做到了 \(O(m+(n+q)\log^2 n)\)

10. World of Tank

考虑 \(n\) 比较小(\(10^6\))的做法,一个想法是 \(dp(i,0/1)\) 表示走到 \(i\) 列第 \(0/1\) 行的最大能量。

问题是怎么处理打格子这个事情,我们不一定打相邻的格子。

一个性质是如果我们打掉了右边一个格子,首先肯定会走到那里,其次中途不会在换行。

那就考虑如果始终只有一行怎么处理使得没有后效性。

发现可以去除能量 \(t\) 的上限,每次用就减去 \(t\),因为只有一行所以这个是没问题的。

现在有两行了,换行的时候,能量和 \(t\) 取个 \(\min\) 就好了。

这样可以做到 \(O(n)\),考虑我们只会在有格子列或者他的前/后一列换行,所以中间的都可以缩掉。

这样就做到 \(O((a+b))\) dp 了,比较方便的实现可以在预处理的时候带个 log,由于输入的坐标递增所以可以归并来去 log,不过这里没有必要。

记录

11. Dreamoon Loves AA

给 dreamoon 磕头了。

给摸鱼酱磕头了。

考虑确定一组 \([\min,\max]\) 如何 check:由于每一段是平均分配的,设一段长度为 \(len\),用了 \(a\) 个,则有:

\[\begin{cases} (a+1)(\min-1)\ge len-a \\ (a+1)(\max-1)\le len-a \end{cases} \]

于是对于 \([\min,\max]\) 和一个 \(len\),可以分别算出 \(a\) 的界 \(L,R\)。则一个必要条件是 \(\sum L \le k\)\(\sum R \ge k\)

发现两部分约束相对独立,所以可以二分出最大的 \(\min\) 和最小的 \(\max\)

为什么到这里没有做完?这个 \(\min \max\) 肯定是一组理论最优解,但是注意:对于有些段,可能不存在一个 \(a\) 恰好满足 \(\min,\max\) 两个人的限制。

因此我们还要调整 \(\min,\max\)

对于每个不合法的段,因为 \(\min\) 会让 \(\ge\) 某个 \(v1\) 的个数全部合法(也就是放的不超过 \(v1\) 个),而 \(\max\) 会让 \(\le\) 某个 \(v2\) 的个数全部合法(也就是放的不超过 \(v2\) 个)。那么当 \(v1\ge v2\) 的时候总能选出一个人同时满足 \(\min,\max\) 的条件,否则 \(v1\lt v2\),我们要么让 \(v2\) 这个人也满足 \(\min\) 的约束,要么让 \(v1\) 这个人也满足 \(\max\) 的约束。

现在变成了一个比较形式化的问题:

有变量 \(x,y\),有若干个约束:要么 \(x\le a_i\),要么 \(y\ge b_i\),最小化 \(y-x\)

可以把约束按照 \(a_i\) 排序,则 \(x\) 必定取到其中的一个(或原始的 \(x\) 值)然后算没被满足的最大的 \(b_i\) 即可。

时间复杂度 \(O(m\log)\)

记录

12. Shojin

考虑如果我们只有一组则根据 exchange argument 可以得到:所有 \(A\gt 1\) 的在所有 \(A=1\) 的前面,后半部分顺序无所谓,前半部分按照 \(\frac{A}{B-1}\) 排序。

更进一步地,可以发现每一段里 \(A\gt 1\) 的不超过 \(\log V\) 个,因此可以对于 \(O(n\log V)\) 种可能的区间(我们忽略 \(A=1\) 的,因为它们的贡献可以直接加在最后)分别以 \(O(\log)\) 的时间内排序算出答案,这部分预处理是 \(O(n\log^2)\) 的。

可以感知到答案关于段数下凸因此考虑用 wqs 二分的思想,求斜率为 \(k\) 的直线和凸包的切点,也就是每分一段答案就 \(-k\)

考虑 dp 的时候首先枚举这一段内 \(A\gt 1\) 的个数,则左端点取值是一段区间。

这个区间 min 的过程不需要用 DS 维护,因为一定是在相邻两个 \(A\gt 1\) 之间的全集(特判 \(cnt=0\) 的情况)。

因此 dp 可以做到 \(O(n\log)\) 单次的复杂度,总时间复杂度为 \(O(n\log^2)\)

记录

13. Strange Covering

一道非常繁琐的题目,但我觉得思维非常巧妙。

首先将坐标离散化并且强制矩形的坐标只能在这些离散化后的坐标内出现。一个矩形可以退化成边/点,但必须非空。为此我们特判只有一个点的情况(因为不确定是否有重复点,所以直接判掉了所有点共线的情况)。

经过讨论,发现可能出现的矩形位置关系有以下几种:

  • 完全相离

  • 十字交叉(不允许退化,也就是四个方向延伸出去的,都必须非空)

  • 对角相交

对角相交分为两种:一个左上一个右下(或者相反)。

比较边界的情况是有重合边但是面积交为 \(0\)。这种情况可以归类到对角相交(如果它们只在一部分边相交是严格不优的,因此公共轮廓部分至少有一个矩形的顶点在其中,那么就比较自然地归类到对角相交了。

对角相交的第二种情况可以令 \(x:=-x\) 然后再做一遍,这样我们只考虑左上右下形。

完全相离是最好判断的:两个矩形一定可以水平/竖直切一刀割开来,以竖直切为例,我们维护所有 \(x\) 坐标 \(\le i\)\(\ge i\))的点里 \(y\) 坐标的极值即可,这部分是 \(O(n)\) 的。

十字交叉较为复杂:考虑在 \(x\) 这个维度扫描线,令当前扫到的 \(x=r\) 作为竖条的左边界。

令整体的 \(y\) 的极差为 \(X\)\(x\) 的极差为 \(Y\)\(X,Y\) 分别对应矩形的高和宽),那么竖条的面积是 \((r-l)X\),而横条的面积还要取决于 \(l\) 的取值。

\(pre_a\) 表示 \(x\) 坐标 \(\le a\) 点的位置里 \(y\) 坐标的最小值,类似定义 \(suf_a\)

找出第一个 \(pre_i\le suf_{r+1}\) 的位置 \(i\),类似对最大值也有这样一个位置 \(j\),那么他们把所有 \(\le r\)\(l\) 划分成了三段。

当然我们知道根据左右 \(y\) 坐标 \(\min,\max\) 的关系分成了四段,对四种情况拆式子上 seg 维护区间 min,然后每次对三种情况取出来算即可。

这一部分的时间复杂度是 \(O(n\log n)\),注意 \(i,j\) 可以双指针算。

接下来来到对角这一块,其实最开始就可以意识到这部分的难度最大,因为他的结构比较复杂。感谢摸鱼酱的指点。

我们考虑如果确定了左上角这个矩形 \(S\),那么右下角的矩形大小有两个部分决定:

  • \(y\) 坐标在 \(S\) 下方的所有点里 \(x\) 坐标最小的点 \(a\)

  • \(x\) 坐标在 \(S\) 右侧的所有点里 \(y\) 坐标最大的点 \(b\)

同时为了保证两个人不能相离,需要保证 \(x_a\) 不能比 \(S\) 的右边界还大;\(y_b\) 不能比 \(S\) 的下边界还小。

枚举 \(S\) 的下边界的 \(y\) 坐标(此时我们不知道右边界的位置但知道他的长度),右边界此时是一个在 \(y=k\) 这条水平线上滑动的状态,设这条滑动线段为 \(T\)

考虑这个时候 \(a\) 也是知道的一个点,则 \(T\) 滑动范围的左边界被限制住了(不能滑动到 \(a\) 的左侧)。

事实上 \(T\) 的滑动范围还有一个右边界:如果它右侧的那个 \(b\)\(y_b\) 太小了,则违反了我们上述的约束,因此可以二分求出 \(T\) 的右边界。

因此,\(S\) 这个矩形的右边界线段(其所在直线是 \(x=k\)) 有一个取值区间在。

两个矩形的四个信息我们已经确定了两个:左上矩形的高度 \(X\),右下矩形的宽度 \(Y\)。我们发现左上矩形的高度 \(A\) 是根据 \(T\) 的位置决定的,而右下矩形的高度 \(B\),是根据 \(T\) 右侧 \(y\) 坐标最大的点决定的。

考虑确定了 \(T\) 也就确定了它右侧最大的点,所以实质上我们有 \(O(m)\) 对(\(m\) 是离散化后不同 \(x\) 坐标的个数)这样的 \((A,B)\)

两个矩形的面积和是 \(AX+BY\),因此现在相当于给出了 \((X,Y)\),在给定区间里找一对 \((A,B)\) 最小化 \(AX+BY\)

它就是最小化 \(A\frac{X}{Y}+B\) 因此可以用类似斜率优化的方式变成给定斜率的直线去切凸壳。

因此我们用线段树维护区间凸壳即可。这样每次询问都要在 \(O(\log)\) 个凸壳上二分斜率,因此是 \(O(\log^2)\) 的。

注意到如果我们按照大小顺序去枚举 \(S\) 的下边界,则 \(\frac{X}{Y}\) 还具有单调性!所以不需要二分,只需要每次把变劣的凸壳点删掉即可。

这样总时间复杂度为 \(O(n\log n)\)。帅!

记录

14. Lanterns

给 EGOI 磕头。

可以设计出这样一个 dp:\(dp(i,j,x,y)\) 表示我们可行动范围区间是 \([i,j]\)\(y\) 坐标上 \([x,y]\) 被点亮了。

在转移的时候发现这样一个事情:若 \([x,y]\) 和我们新拓展的区间不能合并成一个大的,那么这一次就不会拓展它,所以这个 \(n^4m\) 的 dp 正确性得到了保证。

这个还是太抽象了,我们考虑优化一下复杂度。

如果我们只记录 \([x,y]\),则可能有多个可能的行动范围,但是我们只需要再记录其中一个位置就可以了(然后就能根据 \([x,y]\) 来算出左右的拓展区间)。

这样我们做到了 \(n^3m\),很炫酷。

考虑更近一步,把值域和这个 pos 合并一下,其实只要记录说值域区间里 \(L\) 最小的是第 \(a\) 个方案,\(R\) 最大的是第 \(b\) 个方案,那其实 \(i\) 就随便选 \(p_a\) 或者 \(p_b\) 一个都行,所以我们不需要记录 \(i\) 了,因为只记录 \((a,b)\) 就能算出实际的行动范围。

这样就 \(m^3\) 了,超级炫酷。

考虑怎么着这个题都不应该只有一维状态,不然太逆天了。

考虑转移能不能加个速。\(dp(i,j)\rightarrow dp(i,k)/dp(k,i)/dp(k,k)\) 三类。

对于前两类:由于区间 dp 的枚举顺序,当一个 \(dp(i,k)\) 不能给 \(dp(i,j)\) 造成贡献,则往后的所有 \(dp(i,j')\) 都不能从 \(dp(i,k)\) 这里造成贡献,所以对于所有的 \(dp(i,*) + cost\),维护一个堆即可,对 \(dp(*,i)+cost\) 同理。

对于第三类,注意到总共只有 \(O(m)\) 个状态,如果是 \(i:1\rightarrow m,j:m\rightarrow i\) 这种枚举顺序,则当 \(i\) 固定的时候可以用单调栈维护所有的 \(O(m)\)\(dp(k,k)+cost\),然后 \(i\) 变了就重构一次。

这样总时间复杂度是 \(O(m^2\log m)\) 的。

记录

15. Hidden Bipartite Graph

注意到原图连通,那就可以考虑 dfs/bfs 出原图的一个生成树,这样如果是二分图就做完了。

考虑怎么去做这个遍历过程,注意到我们可以花两次代价问出某个点 \(u\) 到某个集合 \(S\)(不含 \(u\))的连边数目。

然后考虑 bfs,在线段树上二分,则每个未访问且连边的点贡献 \(O(\log n)\) 次,这样可以花费 \(O(n\log n)\) 级别的次数求出生成树。

非生成树只需要找一条非树边,容易花费 \(O(n)\) 的额外代价求出。

然后卡一下常数:记忆化,我是直接判了 \(|S|=0\),还有记忆化 \(|S|=1\),如果记忆化所有用过的询问则表现会更加优秀。

记录

16. Restoring Map

开始的想法是问叶子,但是问叶子就不太能问出来。

然后考虑如果有一条边 \((u,v)\) 且两边分别挂了一个点,则它们的交应该恰好是 \(\{u,v\}\),这样可以问出所有的非叶子节点。

然后可以看出一个 corner case 是菊花,这个很好判,因为所有的 \(a\) 都相同。

然后考虑还原所有的叶子信息,首先确定它们的集合:应该是包含他们的最小的。

然后确定每个叶子连谁,事实上叶子集合去掉自己以后可以发现,就是邻居 + 邻居连的所有非叶子节点构成的集合。

这样就在 \(O(\frac{n^3}{w})\) 的时间内完成了,特判两个非叶子节点因为这个时候两个非叶子点的集合完全一致。

记录

17. Pond

给 UM 磕一个。

考虑把贡献拆得独立一点:初始令答案为 \(\sum dis\),然后我们往左边走一步,那么右边剩下的每个点都带来 \(2\) 的贡献。

这样就相当于是折返游走到 \(1/n\) 的最小额外代价,转移是 \(g(j)+2(n-j)(x_j-x_i)\rightarrow f(i)\) 或者反之,这个形式可以维护凸壳。

考虑有互相转移的存在,那么我们用类似 dijkstra 的方式更新即可。又因为 \(f,g\) 的单调性,则只关注没被加入的最右侧的 \(f\) 和最左侧的 \(g\)

时间复杂度 \(O(n)\)

18. Parallel Universes

牛逼消元题。

考虑设 \(dp(i,j)\) 表示说关键点左边有 \(i\) 个人,右边有 \(j\) 个人。

\(dp(i,j)\)\(dp(k\lt i,j)\)\(dp(i,k\lt j)\)\(dp(i+1,j),dp(i,j+1)\) 的线性组合。

考虑移项,得到 \(dp(i,j+1)\) 是剩余项的线性组合,注意 \(dp(i,j+1)\) 能得到这个递推关系当且仅当 \(i+j+1\lt m\)\(\min\{i,j\}\neq 0\)

\(dp(x,j)\)(其中 \(x=1\sim m-2\))这 \(m-2\) 个元素作为基本元,然后用上面的递推关系表示所有数。

这样对于所有 \(dp(i,j)\)\(i+j=m-1\)\(\min\{i,j\}\neq 0\),共 \(m-2\))个,它们不能导出递推关系,利用它们得到的线性组合,以及本身转移式得到的线性组合列方程。

最后做一次高斯消元,复杂度 \(O(m^3)\)

记录

19. Heroes of Might

对每个组,设 \(f(i)\) 表示打它 \(i\) 次和上一次相比死了多少人,则我们会连出一条链式结构,整个问题变成 color a tree 这个经典的 exc-arg。

然后只要两个相邻点的权值(平均数)递增就可以直接合并,最后直接归并在一起即可。

当点数不多的时候(\(\sum a_i\) 小于阈值,这样非 \(0\) 位置不多,合并以后点数很少)很容易暴力;否则 \(h_p\) 不会很大,又因为过程有长度为 \(\frac{h_p}{\gcd(d,h_p)}\) 的周期所以可以考虑用快速幂的方式合并(可能需要特判最后的 \(O(1)\) 段)。

也就是令一个周期得到的是 \(S\),那么计算 \(S\) 复制 \(k\) 次后得到的 \(S'\) 就可以分治。因为两个长度为 \(x\) 的序列合并后长度是 \(x+O(1)\) 的。

令阈值取到 \(\sqrt{V}\) 最优,时间复杂度为 \(O(n\sqrt{V}\log)\)

记录

20. Matches Are Not a Child's Play

直接对每个序列的每个位置开头求 SG 函数,但我们发现 SG 的状态是二维的:\(f(i,j)\) 表示结尾两个人是 \([i,j]\) 的所有状态的 sg 值。

首先 DAG 上的 sg 值不超过其最长路,因为 mex 的话不超过 max+1,又因为这个限制很容易看出 sg 值不超过 \(\sqrt{n}\)

另一方面有一个暴力:倒着枚举 \(j\) 然后计算所有 \(s(i,j)\),那么随着 \(i\) 的变大,能转移到的 \(s(j,k)\) 肯定越来越多,那这就 \(n^2\) 了。

结合上面两点,我们可以把 sg 值塞到状态里,由于 sg 的单调性,我们设 \(f(j,v)\) 是最小的 \(x\) 满足 \(s(x,j)\ge v\),根据 \(f\) 就能描述所有 \(s(x,j)\) 的值(也就是记录分段点)。

考虑如何求 \(g\)?注意到对每个 \(k\) 而言,考察所有 \(s(x,j) = k\) 的,我们只关注最小的 \(k\)。设这个位置是 \(h_j\),则其实 \(f(j,v)\) 利用 \(h_1\sim h_v\) 就能算出来。

但是 \(h_v\) 对每个 \(j\) 而言显然是不一样的,因为 \(s(i,j)=k\) 并不意味着 \(s(i-1,j)=k\),也不意味着 \(s(i+1,j)\),所以这里并没有单调性在。

所以必须对每个 \(v\) 和每个 \(j\) 都维护答案,而当我们修改的时候其实是对一个 \(v\),把区间的答案和当前下标 \(k\)\(\min\);又因为我们是倒着枚举 \(j\) 的,所以它其实只不过是区间未覆盖位置赋值,用并查集即可维护。

时间复杂度 \(O(n\sqrt n\alpha)\)

记录

21. Pond

考虑区间 dp 的话状态就太高了,所以需要摆脱两个状态的问题,变成分别向两侧走。

可以令初始每个人的贡献就是 dis,当我们往一个方向走 \(a\) 步的时候,只需要让另一个方向的所有剩下的人贡献 \(+2a\),这可以在转折的时候实现。

所以设向左走到 \(i\) 的最小额外贡献是 \(f(i)\),向右走到 \(j\) 的最小额外贡献是 \(g(j)\),答案是 \(\min\{f(1),g(n)\}\)

规定 \(f\) 只能从 \(g\) 转移,\(g\) 只能从 \(f\) 转移,这样每一次转移相当于都是转折了一次且刚好把所有额外贡献计算进去了。

然后发现转移是互相转的,可以用 dij 的思想从中心往两边拓展。

这样只需要快速求一个点的值,观察到转移的形式直接用凸包优化就好了,时间复杂度 \(O(n)\)

22. Bosco and Particle

考虑怎么判定是否进入了周期,首先位置得回到 \(0\),同时指针的状态得回到最初始的位置。

但这就错了,如果字符串有一个周期,则指针可以有不止一个位置。

不过发现周期可以直接缩掉,这个用 kmp 预处理一下就好了。这下真的就是回到初始的位置了。

很神的一点是我们只用对于每个人 \(i\) 研究一下左右两条边的经过次数 \((a,b)\),则最后的总答案也满足这个比例(但要注意两个人至少要是 \(a,b\),比如如果出现了 \(2,2\),那么两个人在相同的同时不能小于 \(2\) 的)。

然后就可以设 \(f_i\) 表示 \(i\leftrightarrow i+1\) 的边的经过次数,然后列递推式,只要求最小的合法的 \(f_n\) 即可,合法的意思是不能出现分数,且大于等于本来的 \(a,b\),这个对每个质因子搞搞就行了,时间复杂度 \(O(n\omega)\)

记录

23. Alice and Bob

考虑开 \(n\) 个桶,第 \(i\) 个桶接的是加入这个数以后 LIS 变成 \(i\) 的数,初始每个桶的个数是可以算的;操作相当于取出桶里的一个数然后把这个桶里的大于它的数放到下一个桶,然后 LIS \(=k\) 意味着取了一个第 \(k\) 个桶里的数。

这个转化出来后其实就做完了。

24. Counting Permutations

首先可以发现 \(X\) 的最大量级是 \(O(n\log n)\) 的。

直接设 \(f(i,j)\) 是长度为 \(i\) 然后答案为 \(j\) 的排列个数,这几个 \(n\) 了啊我去。

竟然还在 \(X\) 这个维度卷积的,真恶俗。

最后答案是个 \(n\log n\) 次的多项式,直接代数进去算就不用卷积了...

算一次是 \(n^2\) 所以这部分是 \(n^3\log n\)

最后还要在 \((n\log n)^2\) 的时间内用拉插还原系数。所以是 \(O(n^3\log + Tn^2\log^2 n)\),666。

不过是第一次写还是记录一下:直接对着拉插的那个式子,然后 \(y_i\) 和底下的都是常系数,只考虑 \(\prod (x-x_j)\),这不是我们带删除 0/1 背包计数吗。然后做完了。

25. Segment

好题。

考虑如何快速计算一次询问的贡献,我们肯定得把贡献拆给每个区间。

如果一个区间把 \([l,r]\) 完全(严格)包含,或者和 \([l,r]\) 有交,但没有包含关系;则它显然有 \(1\) 的贡献。

比较麻烦的就是被 \([l,r]\) 包含的那些区间(包括 \([l,r]\)),哪些有贡献?

把所有这部分区间拿出来,发现如果一个区间被完全包含,则它的两个儿子也一定被完全包含,所以这样的区间(点),组成若干颗树,而只有每个树根才会被统计答案。

又因为每棵树都是完全二叉树,所以联想到二度点和叶子的公式:\(c_2 = c_0+1\),那么每棵树里:叶子 - 非叶子的数量恰好为 \(1\),而一棵树的贡献也就是 \(1\)

所以这部分我们这样设置贡献:若一个节点被 \([l,r]\) 包含,如果它是叶子,那么贡献 \(1\);否则贡献 \(-1\)

问题转为求每个 \([l,r]\) 的出现概率。直觉上 \(r-l+1\) 相同应该概率相同,否则无法做;但是 \(n^2\) 打表出来会发现并不满足,不过仔细观察会发现所有不靠边界的区间(\(l\neq 1\land r\neq n\)),概率只和 \(r-l+1\) 有关。

到底是怎么求的?考虑我们只关注每个空格 \((i,i+1)\) 断开的相对顺序,所以相当于是生成一个 \(n-1\) 的排列,使得两侧的两个空格标号出现在中间的所有空格标号之前的方案数,容易简单 \(O(n)\) 预处理每个长度的贡献,靠边的也类似。

然后剩下的就很好统计了:我们先统计所有和 \([l,r]\) 有交的区间(以下的区间都有 \(len\ge 2\)),它们的贡献是 \(1\);然后去掉所有被 \([l,r]\) 包含的区间,它们的贡献是 \(-2\);特判边界上的区间即可。

时间复杂度 \(O(n+q)\)

26. 线段

洛谷高质量 DS

有一个部分分:\(l\le 10^5\le r\),这代表所有线段(包括询问给出的)全部在 \(10^5\) 处有交。

那么在两侧分开来,独立维护即可。

注意到我们在左侧要做这个事情:把 \(\le x\) 的人设置成 \(x\),全局求和。暴力的复杂度肯定是不对的,考虑用并查集去维护即可。

回到一般情况,根据上面部分的提示,我们考虑用猫树分治的思想来做。

也就是把每个存在的区间挂在猫树分治对应的节点上;查询其实是容易的:每条线段给 \(l\sim r\) 区间加一,然后 \(ql\sim qr\) 的和就是答案;重点考虑修改,也就是所有和 \([ql,qr]\) 有交的线段都变成和 \([ql,qr]\) 的交。

直接在 seg 上定位 \([ql,qr]\),如果当前节点 \([l,r]\) 完全被 \([ql,qr]\) 包含,则挂在这个节点(以及其子树内)的线段都不会有任何变化。

否则分两种情况来处理挂在 \([l,r]\) 的所有线段,然后继续把 \([ql,qr]\) 递归下去。

  • 如果 \([ql,qr]\) 跨越了 \(mid\),类似最开始的部分,我们只需要把左半部分所有 \(\le ql\) 的线段的左端点修改,然后把右半部分所有 \(\ge qr\) 的线段的右端点修改,拿 dsu 记录一下即可,注意此时对 bit 的修改也是容易的。

  • 如果 \([ql,qr]\) 只在左半侧或者右半侧,以它在右半侧为例,则所有 \(r\ge ql\) 的区间都会被取出来,然后和 \([ql,qr]\) 取交后丢到右半部分的区间重新找到新的猫树节点;由于每个区间递归 \(\log m\) 次就到叶子,所以总递归次数是 \(O(k_1\log m)\) 的。

那么由于第二种情况中我们需要支持取出 \(r\) 最大的若干区间 / \(l\) 最小的若干区间并且删除;可以每个节点用 dsu 维护左/右端点的相等关系(应对第一种情况);然后所有出现的左/右端点权值用两个堆来维护。

时间复杂度应该是 \(O(k_1\log^2 m + k_2 \log^2 m + k_3\log m)\)

记录

27. Innocence

呃呃。

考虑 \(L=1\) 怎么做?之前好像这个 trick 云浅提到过:就是我们可以让前 \(n-1\) 个人任意选,只要最后一个人合法就行。

但是直接这样套肯定套不上去,还是考虑有一个比较特殊的人,和 \(n-1\) 个随便选的人。

考虑最高位,如果 \(r\) 的最高位是 \(0\) 我们摆烂;否则除非所有人这一位都填 \(1\)(这种情况就递归下去做),不然的话肯定有一个人这一位填 \(0\)

考虑后面的位,另外 \(n-1\) 个人随便填,然后剩下的这个人就唯一确定了填法,而且不管怎么填都是合法的。

这样就可以 \(O(\log)\) 算答案了。

\(L\) 的情况就容斥一下。

但是会发现一个问题就是“所有人这一位都填 \(1\) 的情况”,如果 \(L-1\)\(R\) 的最高位都是 \(1\),那其实不太能递归下去分成子问题。

考虑直接枚举一个位 \(p\),使得它是从高往低,第一次出现有一个人没顶到上界的(也就是上界在这一位是 \(1\) 但这个人填了 \(0\))。然后特判一下所有人都顶到上界的情况。

然后因为这个时候有 \((L-1)/R\) 两种抉择,所以不太好 \(O(1)\) 算答案,得用矩阵快速幂了。

具体而言我们关注的是:有没有人没顶到上界 + 当前这一位的奇偶性 + 多少个人选了 \(L-1\)(用来确定更高位的 xor 是否 \(=k\)),有 \(8\) 个状态。

每次都跑矩阵快速幂算算是过不去的,但是每组数据的每个询问,矩阵其实是一样的(只和 \(L,R\) 有关),可以提前预处理出来。

28. Doping

tibinyte 出的牛逼题,磕了。

没有 \(rk(a)\lt rk(p)\) 的限制就容斥一手,也就是设 \(g(i)\) 是分出了 \(i\) 段,然后有个反演关系在,不过长得感觉不太像二项式反演。

\(rk(a)\lt rk(p)\) 的话不得不枚举 lcp 了,然后考虑后面的部分,分类:分成 \(\lt p_i\) 的和 \(\ge p_i\) 的两类。

分别有 \((a_L,b_L)\)\((a_R,b_R)\) 两个二元组,表示说两部分分别初始就有 \(a\) 个连续段,然后有 \(b\) 个位置可以切一刀再加连续段。

注意我们先不考虑 \(p_{i-1}\)\(p_i\) 的合并,因为这个肯定是比较弱的。也能规约成,上面的形式。

那就是 \(\dbinom{b_L}{k_1}\dbinom{b_R}{k_2}(k_1+k_2+a_1+a_2-1)!\times \frac{k_1+a_L}{k_1+k_2+a_L+a_R}\) 贡献到 \(a_L+a_R+k_1+k_2\) 这个位置。

直接枚举 \(k_1+k_2 = v\),发现把常数提取出来以后只剩 \(\sum \dbinom{b_L}{k_1}\dbinom{b_R}{v-k_2}(b_L+k_1)\)

如果忽略掉后面那个,其实就是个范德蒙德卷积;然后把 \(b_L\) 这个常数相关的也扔掉,\(\sum k_1\dbinom{b_L}{k_1}\dbinom{b_R}{k_2}\) 咋求?

利用吸收恒等式导出的结论 \(m\dbinom{n}{m} = n\dbinom{n-1}{m-1}\) 即可,要判一下边界。

那看上去把最后的 \(g\) 反演一下就做完了?

错误的,因为考虑 lcp 这里的连续段,这部分还没有做容斥,因为这些段其实,也能任意切割的。(只不过不能变换相对顺序),所以现在又变成 \(n^3\) 了,枚举 lcp 后还得 \(n^2\) 计算对容斥的贡献。

然后有一个很神的做法!我们倒着枚举 lcp,假设枚举 \(lcp=i\),此时我们 \(g\) 数组维护的是 \(lcp=i+1\sim n\) 情况的,然后如果 \(a_{i}=a_{i+1}\) 它们都可以选择在这里切割(多一段)或者保留,大概是这样。

然后就 \(n^2\) 了,最后一步很神!

记录

29. Maximum Product

感觉纯粹是位置原因,够不到 3500* 的 diff。

首先考虑一个集合 \(|S|\)\(f\) 怎么算,我们直接取绝对值最大的 \(m\) 个,如果结果是负数,有两种调整:

  • 删去绝对值最小的正数,加上绝对值最大的负数。

  • 删去绝对值最小的负数,加上绝对值最大的负数。

可以发现调整以后权肯定非负(特殊情况有两种:集合大小全是负数且 \(m\) 是奇数,还有就是 \(|S|=m\))。

然后按照绝对值排序。

枚举四个位置 \((i,j,k,l)\):分别表示排序后前 \(m\) 个里最靠后的正数/负数位置,以及后 \(|S|-m\) 个里最靠前的正数/负数位置。

然后可以 \(O(1)\) 计算答案。这样是 \(n^4\)

考虑优化:枚举 \(i,j,k\) 后,调整 \((i,l)\)\((j,k)\) 优的是一段区间,可以双指针出来。

这样就到 \(O(n^3)\) 了。

记录

posted on 2023-06-01 23:55  Cry_For_theMoon  阅读(179)  评论(1编辑  收藏  举报