P2476
很好的绿 dp,场上卡我 2h,场上只考虑了二进制状压,然后组合数填数,最后发现没法去除重复情况。
说一下简单的正解,这题组合数也是能搞的,只是需要多开一维记录当前存在多少相邻的同色位置。考虑到 \(c_i \leq 5\),我们记录每种颜色个数的个数,然后按照个数记忆化爆搜,这样一想就很简单了。具体地,我们令 \(f(a,b,c,d,e,las)\) 表示 1 - 5 每种颜色还有多少,上一次是在哪一类数量选的,因为一个数字用完之后会扔到下一维,防止两种相同颜色在一起,所以要减掉 \(1\)。
code
P2051
以为是挺简单的 dp,结果场上没写出来。
发现每一列/行只有三种情况,有 0/1/2 个棋子,假如我们枚举行,限定行的放置个数,就只需要考虑列的情况了,具体地,我们只需要维护前 \(i-1\) 列的放置情况即可。令 \(f(i,j,k)\) 表示前 \(i\) 行,\(j\) 列有一个棋子,\(k\) 列有两个棋子,另外没有棋子的列可以表示为 \(m-j-k\),可以直接考虑转移。
当新拓展的一行不放棋子的时候: $f(i+1,j,k) = f(i,j,k) $,
当新的一行放一个棋子的时候,分类讨论是增加哪种列:
\(f(i+1,j+1,k) = f(i,j,k)\times (m-j-k)\)
\(f(i+1,j-1,k+1) = f(i,j,k) \times j\)
放三个棋子的时候同理讨论转移:
\(f(i+1,j+2,k) = f(i,j,k) \times \binom{m-j-k}{2}\)
\(f(i+1,j,k+1) = f(i,j,k) \times j \times (m-j-k)\)
\(f(i+1,j-2,k+2) = f(i,j,k) \times \binom{j}{2}\)
注意取模,统计答案即可。
code
P5304
之前集训的时候就听过的 trick,没想到这场考了,竟然还是不会,我是菜b。
首先,我们暴力枚举再最短路的复杂度是 \(O(k(n+m)\log m)\)。显然是爆炸的,我们考虑二进制分组,我们每次划分两个集合 \(\left \{A\right \}\) 和 \(\left \{ B \right \}\),按位枚举关键点,这一位为 \(1\) 的扔到 \(A\) 集合,否则扔到 \(B\) 集合,再对一个集合连超级源点,跑一遍最短路即可。会发现,假如答案的左右端点不同,那么他们至少有一位是不同,所以会有至少一次被跑最短路,答案是正确的,复杂度 \(O(Tn\log n\log k)\),虽然还有 \(O(Tn\log n)\) 的做法,但这种 trick 倒是很神奇的。
注意两个集合需要互相跑最短路,要建两个图。
code
AT_dp_q
场上还以为能评个蓝,一开始看到有人 10min 过了,吓一跳,疯狂写,然后假了一次 (。
考虑设计 \(f(i)\) 表示以 \(i\) 位置结尾的上升子序列的最大价值和,转移是 \(f(i) = \max_{a_j < a_i} \left \{ f(j)\right \} + h_i\),然后就可以用线段树维护 \(f\) 数组的前缀 \(\max\) 和单点修改即可,这种东西卡了我 40min,菜死了菜死了菜死了菜死了。
code
P6273
上场卡死我的唐题,对标 2023CSP-S T2,真唐完了,今年不会 CSP 还是个 2= (。
场上考虑了类似消消乐那题的答案合并,发现两个答案区间并起来也是合法的,所以我们可以用类似消消乐的方式跳链表维护,但显然这样是假的,随便构造一组类似 baaaaaaaa...
的样例就卡爆了。
正解,注意到,令 \(s_{i,k} = \sum_{j=1}^{i} [S_i = k]\),则我们判断一个区间合法的标志就是 \(\forall a \in T,pre_{r,a}-pre_{l-1,a} = k\),此处 \(k\) 是一个正整数,换句话说,假设我们固定一个字母用来表示 \(k\),则这个式子写作 \(pre_{r,a}-pre_{l-1,a} = pre_{r,b}-pre_{l-1,b}\),把式子移项,得到,\(pre_{r,a}-pre_{r,b} = pre_{l-1,a}-pre_{l-1,b}\) 那么我们只需要记录每个位置每个字母和这个固定字母的差值,哈希一下扔进 map
里面,记个数即可。
code
P10083
很好的体现了场上没用草稿纸的问题,考虑分析区间合法的充要条件。
-
区间的总和不能为负,即 \(\sum_{i=l}^{r} (b_i-a_i) \ge 0\),记作一式
-
任意时刻,我们都必须能打出下一张牌,最坏情况就是把所有负收益的放在前面打出来,即 \(E - \sum_{i=l}^{r} \max \left \{ 0,a_i-b_i\right \} \ge \max_{i=l}^{r} a_i[a_i<b_i]\)
-
进一步考虑,在上述情况下,我们还要考虑负收益的牌这时候能不能打出来,最坏情况肯定就是它是最后一张负收益的牌,然后判断是否合法,即 \(E-\sum_{i=l}^{r} \max \left \{ 0,a_i-b_i\right \} \ge \max_{i=l}^{r} b_i[a_i > b_i]\)
整理一下后两个式子,写作 \(E-\sum_{i=l}^{r} \max \left \{ 0,a_i-b_i\right \} \ge \max_{i=l}^{r} \min \left \{ a_i,b_i\right \}\),记作二式。
则我们需要同时满足两个式子,分别讨论:
-
对于第一个式子,令 \(pre_i = \sum_{j=1}^{i} (b_i-a_i)\),则第一个式子为 \(pre_r-pre_{l-1} \ge 0\),即 \(pre_r \ge pre_{l-1}\)。
-
对于第二个式子,可以发现,左边式子单调不增,右边式子单调不减,存在这样的性质,我们可以直接双指针维护。
具体地,我们可以拿 ST 表维护二式右边式子,然后每次双指针拓展到最远的 \(r\) 点,用 BIT 离散化前缀和后计数即可。
code
P10930
两个多月之前做的了,题挺好的,写一下。
现在我们要在树上支持三个操作。加特殊点,抹除特殊点,求特殊点路径覆盖边的长度。这种东西看起来挺难搞的,那我们就一步一步模拟着来看。
人类直觉做法,发现所有情况可以分类讨论,假定当前点为 \(u\),我们肯定是要找他前后最优的点来计算路径,这里是可以直接二分的,可能会发现有情况满足取的不会是最近的点,但是这种是不影响结果的,放到了下面去讲。
只有前驱或者只有后继的情况没什么好讨论的,我们考虑一般情况。
拟定当前的根有三棵子树,每棵子树下挂着一堆点,令 \(u\) 在中间子树的某个节点上,他下面也有子节点。
画出图类似这样,我们记根的三棵子树分别为 \(x\),\(y\),\(z\),特别地,我们令 \(y\) 子树内 \(x\) 往上到根的部分记作 \(v\),往下的部分记作 \(w\)。
此处以增加为例,删除同理,讨论三个点的位置关系,前驱为 \(pre\),后继为 \(nxt\):
-
\(pre\) 在 \(x\) 子树内,\(nxt\) 在 \(w\) 内,就不画图了,抽象理解一下,我们需要加上 \(dis(pre,x)+dis(x,nxt)-dis(pre,nxt)\)。
-
\(pre\) 在 \(x\) 子树内,\(nxt\) 在 \(z\) 子树内,我们需要加上 \(dis(pre,x)+dis(x,nxt)-dis(pre,nxt)-dis(x,root)\),此处 \(root\) 为根节点。
-
\(pre\) 在 \(v\) 内,\(nxt\) 在 \(w\) 内,同情况一。
-
\(pre\) 在 \(v\) 内,\(nxt\) 在 \(z\) 内,我们需要加上 \(dis(pre,x)\)。
我们将每种情况的加减都变为 \(dis(pre,x)+dis(x,nxt)-dis(pre,nxt)\),情况一三不变,情况二少了 \(x\) 到根的答案,情况四多算了 \(dis(x,pre)\),发现这两种情况下我们如果加上 \(dis(pre,nxt)\),答案就是正确答案的二倍,进一步拓展这个结论,通过拼接几种情况,我们最后需要加上 dfn 最小的到 dfn 最大的路径长度就能使得所有边正好被经过两次,这个可以自己去手动模拟一下,这种结论挺抽象的,可以类似环尾再走到环首。
还有上面说到取的不是最近点的问题,就是类似情况三的时候,子树 \(z\) 上还有点距离 \(x\) 更近,发现此时我们按照上述方法更新的话增加这个点和没有增加的路径长度是一样的,所以说对答案并不会产生影响。
用 set
维护特殊点,二分 dfn 序再算答案就好了,复杂度 \(O(n\log n)\)。
补:过了 P3320 这个结论应该会更好理解一些,就是走过去再从终点走回来,每条边都被覆盖两次,再除掉就好了。
code
P4137
一个序列,每次询问区间 \operatorname{mex}。
\(1 \leq n,m \leq 2\times 10^5,1 \leq V \leq 2\times 10^5\)。
回滚莫队基础练习题,可是我们不会回滚莫队,考虑另类做法。
使用回滚的原因,是 \(\operatorname{mex}\) 不好进行添加操作,复杂度容易退化,我们考虑一种方式维护增加,首先很好想的就是权值线段树上二分找前缀 \(1\) 的和是否等于区间长度,这个实现起来也比较简单,可是复杂度是 \(O(n\sqrt n \log n + n\log ^2 n)\),对于 \(2e5\) 的数据是无法通过的。
在思考,发现这种算法的瓶颈还是在于添加的 \(\log\),我们考虑使用添加 \(O(1)\) 的数据结构再维护,没错,还是分块。我们考虑对于值域再次分块,这样就可以直接进行修改了,查询时也不需要二分,直接便利每一个块看哪一个块中有空缺的与元素就好了,这样复杂度是 \(O(n\sqrt n + n\sqrt V)\) 的,其中 \(V\) 是值域,可以通过,而且跑得飞快。
code
ABC298E
两个人轮流取数,分别有 \(A\) 和 \(B\) 的初始值,第一个人从 \([1,P]\) 中等概率取数,第二个人从 \([1,Q]\) 中等概率取数,求第一个人获胜概率。
\(1 \leq n \leq 100,1 \leq P,Q \leq 10\)。
开学之后感觉就开始颓了,做点简单题。
概率 dp,我不会的东西,考虑大力 dp,设 \(f(i,j)\) 表示 A 已经有 i,B 已经有 j 的概率。
直接大力转移:\(f(i+\min\left \{n,i+x\right \},j+\min\left \{n,j+y\right \}) += f(i,j) \times \frac{1}{p\times q}\)。
复杂度 \(O(n^2pq)\),完全能过。
code
P8025
给定一棵 \(n\) 个点的树,初始在 \(m\),每次有指令 \((d,k)\) 表示沿着最短路径向 \(k\) 号点最多走 \(d\) 步,能走到就停下,走不到就停在最近位置,每次输出当前位置。
\(1 \leq n,m,k \leq 10^6\)。
感觉好久没更新了,最近做题好慢啊,写简单题。
题意就是每次树上俩点走 \(k\) 步走到哪,不能到达就输出最远的点。
这东西显然是可以大力树剖的,发现转折点是一个关键的点,前半段上升,后半段下降,分类讨论就好了。记 \(dep_u\) 表示节点 \(u\) 的深度,那么从 \(u\) 点走到 \(lca\) 的步数就是 \(dep_u - dep_{lca}\),下降同理。
-
当 \(dep_u-dep_{lca} < k\) 的时候,说明一定走不到,我们直接考虑跳链,一条链的链头记作 \(tx\),当 \(dep_{u}-dep_{tx} < k\) 的时候,说明这一条链都不符合答案,继续跳即可,反之则答案就在这条链里面,答案是显然具有单调性的,直接在链上二分即可,这里就是树剖的性质,一条链上的节点编号是连续的。
-
当 \(dep_u - dep_{lca} = k\) 的时候,答案就是 \(lca\)。
-
否则,答案就在下降路径上,上升路径的步数是固定的,直接减去即可,然后类似上升过程中的跳链做法,判断步数在最后一条链上二分找答案即可。
复杂度 \(O(n\log^2n)\)
code
P1654
给定一个序列,每个位置表示 \(i\) 有 \(a_i\) 的概率为 \(1\),连续的 \(x\) 个 \(1\) 产生 \(x^3\) 的贡献,求答案期望。
\(1 \leq n \leq 10^5\)。
不会期望,所以刷点水期望 dp。
发现这个连续段不好搞,转成一个一个增加来看,假如有原答案 \(x\),我们新增加一个 \(1\),那么答案的变化是 \((x+1)^3 = x^3+3\times x^2 + 3\times x +1\),相对于原答案增加 \(3\times x^2 + 3\times x +1\),然后对 \(x\),\(x^2\),\(x^3\) 分别 dp 就好了。
code
CF1997E
给定一个序列,有一个阀值 \(k\) 和当前力量 \(x\),循序遍利序列,当且仅当 \(a_i \leq x\) 的数量累计大于等于 \(k\) 时,\(x \gets x+1\),给出 \(q\) 次询问,每次查询当阀值为 \(k\) 时,\(a_x\) 是否小于遍历到它时的 \(x\)。(后补题意,可能与原题有出入,建议看原题)
\(1 \leq n,q \leq 10^5\)。
差点场切的 *2200,一个月前做的,补一下题解,虽然也是一个多月前写的(。
写一下 E 吧,毕竟想了我好久。不会正解 BIT,写暴力根号分治,也就比正解慢了 1000ms 而已(?)。
首先,我们能够想到一种暴力,我们离线下来询问,依次按照 \(i\) 和 \(x\) 排序,然后我们对于每一种 \(i\) 处理答案,对于 \(i\) 的种类少的数据跑得很快,但是这种做法容易被 \(i\) 多的极端数据卡到 \(n^2\)。然后,我们再考虑优化一点的方案,对于每次够了 \(k\) 次,我们都可以二分出这个位置,查询区间内可以用主席树做到 \(\log^2\),看着感觉复杂度挺对的,可是对于一堆 \(i\) 很小的数据,我们的二分可能会退化,也有缺陷。考虑将这两种算法结合,对于 \(i \leq V\) 的查询,我们可以用第一种方案暴力算,其中 \(V\) 是阀值,然后这样由于阀值的限制,第一种的复杂度就可以控制在 \(O(\sqrt n)\),然后 \(i > V\) 的用第二种算,还是因为阀值的限制,复杂度可以得到控制,这样做总体的复杂度是 \(O(n\sqrt n + n \log^3 n)\) 的,会发现,被卡了 (。
考虑哪里还能再优化,发现当 \(i>V\) 的时候,我们的等级最多会升到 \(\frac{n}{V}\),这就解决了前缀和爆数组的问题,简单算一下,\(V\) 取到 \(n\log n\) 的时候最好,数组开到 \(2e5\times 120\) 就够了,然后先按照每个数算一遍前缀和,再记一个前缀和扫另一个前缀和,相当于扫了一个二维平面吧(?),就可以做到 \(O(1)\) 的获取区间答案了,少了两只 \(\log\),复杂度 \(O(Vn+n\sqrt {n\log n} + \frac{nm}{V}\log n)\),跑了 1400+ ms,挺好的。
code
P5648
给出一个序列和多组询问 \((l,q)\) ,求 \(\sum_{i=l}^{l+q-1} \max_{l\le j\le i}a_j\),数据要求强制在线。
\(1 \leq n,t \leq 5\times 10^5\)。
感觉是很神奇的题,首先会有一个跳 \(\max\) 的思路,可是这样显然会被类似单调递增序列卡掉,我们考虑如何优化。
发现这种跳的过程我们很类似的会用在倍增求 LCA 中,我们往哪方面想,假如我们将一步跳跃看做树上一跳向上的边,那么我们最后的答案就是求树上前缀距离了,这就很简单了。
神奇的,code
P5789
一张图,从 \(1\) 号点开始,每次可以走到一个相邻点,不动,停止这一种移动方案,求 \(t\) 次后的移动方案总数,对 \(2017\) 取模。
\(n,m \leq 100,t\leq 10^9\)。
口胡成功力。
先不管它是怎么走的,先考虑一个点可能怎么走来,显然就是和它相连的点的方案数相加对吧,写成转移方程,令 \(f_i\) 表示第 \(i\) 个点的方案数,那么每增加一秒,他的答案就变成 \(f_i = f_i + f_{j_1} + f_{j_2} +...\),这种东西就很像数列加速的样子对吧,所以我们直接写成矩阵,\((i,j)\) 为 \(1\) 当且仅当他们之间有边,由于 \(n,m\leq 100\),所以这样做是对的。
再考虑,还有自爆和不动的情况,自爆我们可以连一个点到外面,然后让那个点连自环,然后再让每个点跟自己相连,就处理了不动和自爆的情况,就完了。
code
ABC332F
给定一个长度为 \(n\) 的数组和 \(m\) 次操作,每次操作会给出三个整数 \(l,r,x\),你要从 \(l\) 到 \(r\) 中等概率的选一个数并将他替换成 \(x\),问最后每个数的期望值。
\(1 \leq n,m \leq 2\times 10^5,1 \leq V \leq 10^9\)。
简单期望,期望做的还是少,还在考虑和后面有一点捆绑关系。
考虑当一个区间新增时,对于一个在区间内的值 \(val\) 的修改就是令他变为 \(val \times \frac{r-l}{r-l+1} + k \times \frac{1}{r-l+1}\),然后还有区间修改,那么这就是一棵区间乘,区间加,单点查询的线段树了。
code
P3979
一棵树,支持三个操作:
- 将根变为 \(x\)。
- 将 \(x\) 到 \(y\) 的路径所有点权值赋为 \(v\)。
- 求当前根下 \(x\) 子树内的最小权值。
\(1 \leq n,m \leq 2\times 10^5,1\leq V \leq 2^{31}-1\)。
经典 tick 树剖换根,虽然树剖没有办法直接换根,但是我们可以分类讨论解决。
简单分析可得,记当前点为 \(u\),根为 \(root\),我们以 \(1\) 号点建树剖,假如 \(u\) 在 \(1-root\) 路径上,那么换根对树剖就是有影响的,此时的答案区间就是不包含 \(root\) 所在的 \(u\) 子树区间,取个补集就好了,其他情况没变化,注意特判 \(root\) 和 \(u\) 重合的情况。
code
ABC371F
\(n\) 个点分别位于 \(x_i\),保证 \(x_i\) 单调递增,每个点有目标点 \(y_i\),要求点的顺序关系不改变,每个点每时刻可向左右移动一单位,求所有点依次归位的最小步数。
\(1 \leq n \leq 2\times 10^5,1 \leq q \leq 2\times 10^5\)。
好题。为什么好?因为我不会。
首先能够想到每次向左向右会使得一整块棋子向目标点右边偏移,这就需要我们二分处理,是可以做的,处理之后,区间推平,再覆盖一个等差序列就好了,这样做是可以的,但是麻烦,考虑简单做法。
第一次见这个 trick,考虑令 \(a_i \gets a_i - i\),那么我们推平等差数列的操作就变成区间赋值了,然后我们二分向右找是 2log 的,这里选择线段树上二分,虽然这也是第一次学,但是也是简单的,其实线段树上二分就和普通二分差不多,还是要求单调性,每次的决策扔到线段树节点上就好了,跟普通二分的决策方式是一样的,可是线段树二分的复杂度是 \(O(n\log n)\) 的,总复杂度 \(O(n+q\log n)\)。
code
P8817
给定一张 \(n\) 个点 \(m\) 条边的图,选择 \(4\) 个不同点作为转移点,要求每个转移点之间最短距离不超过 \(k\),每个点有价值,求转移点价值最大和。
\(1 \leq n \leq 2500,1 \leq m \leq 10000,1 \leq k \leq 100\)。
赛前写写真题了属于是,还是挺简单的。
由于每段路程都是分开的,只有选点不能重复这一个条件,我们可以把所有点走 \(k+1\) 步能走到的点都找出来,复杂度是 \(O(n^2)\),用 vector
存下来,发现这样就可以把图缩小,每条边连接的点代表他们之间的距离小于等于 \(k\),这与原图是显然等价的。我们现在只需要考虑选点不重复这一个难题了,猜想复杂度肯定是 \(O(n^2)\),发现可以枚举中间两个点 \(BC\),然后 \(A\) 的取点范围就是 \(1\) 和 \(B\) 取点的交集,\(D\) 的取点就是 \(C\) 和 \(1\) 取点的交集,取交集这一步也可是 \(O(n^2)\) 完成,然后直接贪心就好了,我们直接拿点集最大值进行匹配,当有重复点时,取次大点即可,可是发现可能 \(B\) 的次大值也与 \(C\) 重复,所以还要取一个次次大值,然后拿两边的 \(3\) 个值互相比较就做完了,复杂度 \(O(n^2)\)。
一发过,好耶。
code
P11253
给定 \(n,k\),求 \(\sum_{i=1}^n \frac{i!}{i^k}\) 对 \(998244353\) 取模。
\(1 \leq n,k \leq 2\times 10^7\)
开颓,不会绿。
感觉很抽象啊,注意到 \(i!\) 可以 \(O(n)\) 预处理,并且 \((ij)^{-k} = i^{-k} \times j^{-k}\),所以,这东西可以欧拉筛筛出来,因为质数个数约为 \(\frac{n}{\log n }\),所以复杂度是 \(O(n)\),要卡一卡常。
code
CF809C
给定一个 \(10^9 \times 10^9\) 的矩阵,左上角为 \((1,1)\),每个点的权值被定义为其左上角内本行本列的 \(\mex\) 值,要求每次询问一个子矩阵的和。
\(1 \leq q \leq 10^4\)。
模拟赛场上会了单点求值的办法,没想到前缀求和,无敌了。
一种不用注意力的方法,通过打出 \(30 \times 30\) 的表来可以发现这是个分形的东西,一大个大矩形的长度为 \(2^k\),那么他就可以被分为边长为 \(2^k\),\(2^{k-1}\),\(2^{k}\),\(2^{k-1}\) 的四个小矩形,而且可以观察得出大矩形的每一行都是一个 \(2^k\) 的排列,你甚至还可以得出他的分形规律,但对正解没什么用。
每次询问一个矩形的答案,自然可以差分变成询问四个左上角在 \((1,1)\) 的小矩形的答案,现在就要考虑怎么求一个覆盖左上角的矩形的答案。
由于这个东西的特性,我们先将他向外补到 \(2^k\),然后讨论他右下角点的位置:
像这样,假设他在右下角,那么蓝色部分就是一个完整的边长为 \(2^{k-1}\) 的矩形,通过之前观察发现他的每一行都是 \([1,2^{k-1}]\) 的排列,所以可以直接求和,对于红色部分的两块多余部分,我们发现看他们的长边,都是 \([2^{k-1}+1,2^k]\) 的排列,他们也可以直接算出来,然后还剩下最后的绿色部分,由于我们前面补出的是一个正方形,所以绿色部分肯定在主对角线上,所以他左上角的值是 \(1\),这样就可以继续向下递归求解这个小矩形的答案。
类似的,假设这个点在左下角或者右上角,那么他就只剩向左/上的红色部分,这个跟上面一致是可以算出来的,然后在递归小矩形的答案,但是由于他不在主对角线上,所以他的值会有一定偏移量,其实就是 \(2^{k-1}\),递归时也要加上。
假设这个点在左上角,就是当前区间取大了,还可以取到更小的正好包含这个点的区间,所以令 \(k\) 缩小即可。
代码很好写,注意取模。
submission
P1169
一个 \(n\times m\) 的 01 矩阵,找出最大的完全 01 相间的矩阵和正方形。
\(1 \leq n,m \leq 2000\)。
跟上一场模拟赛 T4 一样的 trick 啊,悬线法 dp,解决二维 dp 中的某些求矩形的问题。
以本题为例,我们要求一个最大的 01 交错的矩形和正方形,直接做肯定是不好搞的,包括什么二分答案之类的,因为你不好写 check,而暴力 check 是 \(O(n^2)\) 的,肯定爆炸。
考虑一种不一样的 dp 方式,定义 \(f_{i,j}\) 表示点 \((i,j)\) 向左最多到哪里是 01 交错的,同理 \(g_{i,j}\) 表示向右的,然后定义 \(up_{i,j}\) 表示这个点向上最多扩展几个,考虑如何转移。
显然,因为我们要找的是一个矩形,边长肯定是一定的,按照一段上下连续的 10 序列来看,要求这个序列中最小的 \(g\) 和最大的 \(f\),就可以当作横边长了,然后连续段长就是纵高,就可以很好的维护面积了。
具体来说,先横向处理,当当前位置和前一个位置颜色不同时,
之后,纵向处理,当这个点与上一行的颜色不同时,
代码很好写,code
P9219
交互,一个隐藏序列保证有且仅有一个 \(i\) 使得,\(a_i > \max_{j\neq i} a_j\),找出这个 \(i\),询问次数 \(\leq \left \lfloor \frac{n}{2}\right \rfloor +2\)。
\(1 \leq n \leq 5\times 10^4,1 \leq V \leq 10^8\)。
期末考完滚回来写题了,啥都不会了,菜的没谱。
感觉比较有意思的交互。
考虑保证有一个数不小于其他任何一个数的 \(2\) 倍这个性质很重要,假设说,现在有三个点 \(x,y,z\),已知 \(|x-y| > |x-z| > |y-z|\),说明此时 \(z\) 在中间,然后左边是 \(y\),右边是 \(x\),第一步 \(z\) 很好确定,那么 \(x\) 和 \(y\),我们发现,假设交换过来,\(y\) 在右,那么显然是不满足 \(2x \leq y\),因为向右走还有一个 \(z\),感性理解一下,放到数轴上就好看了,比较懒,就不放图了。
有了这个结论之后,我们可以对 \(n\) 个数两两配对,那么答案点一定在差值最大的一组点里,这个同理也是可以得到的,然后我们还有两次判断的机会,对于 \(n\) 是偶数,直接找一个点判断位置关系就好了,对于 \(n\) 是奇数,我们还剩一个没有用的点,把这个点拿出来与最大点组判断位置关系就好了,判断次数刚好用完了。
code
P2258
给定一个 \(n\times m\) 的矩阵,从其中选一个 \(r\times c\) 的矩阵,使得矩阵的价值最大,价值定义为上下左右元素的差绝对值和。
\(1 \leq r,c \leq n,m \leq 16,1 \leq V \leq 10^4\)。
感觉状态好一点了,虽然没有独立切掉。
暴力显然就是枚举行枚举列,因为确定了行数和列数,所以复杂度是 \(O(C_{n}^{r} C_{m}^{c})\),无法通过。
考虑优化,我们只枚举一维,假设枚举行的选的情况,我们只需要确定一个列的选法的最大值,也就变成了一维的问题,此时设计 \(f_{i,j}\) 表示第 \(i\) 列,已经选了 \(j\) 列的最小分数,枚举转移过来的上一列 \(k\),这样我们就可以 \(O(rc)\) 的暴力计算分数,然后转移和前面的枚举是 \(O(C_{n}^{r} m^2)\) 的,所以复杂度 \(O(C_{n}^{r} m^2 rc)\),可以通过。
code
P3874
一个 \(n\) 个点的树,每个点有代价 \(w_i\) 和价值 \(v_i\),至少选 \(k\) 个,求最大性价比。
\(1 \leq n \leq 100,1 \leq V \leq 10^4\)
01分数规划 + 树形dp。
先说一下分数规划吧,大概是要解决一个形似 \(\frac{\sum a_i}{\sum b_i}\) 的问题。
对于这个东西,我们直接二分答案,则有 \(\frac{\sum a_i}{\sum b_i} > mid\),即 \(\sum a_i - mid \times \sum b_i > 0\),也就是 \(\sum (a_i - mid \times b_i) > 0\),判断这个条件成立即可。
放到本题,基本与上述相同,每次二分答案平均值,将所有点的价值改为 \(v_i - mid \times w_i\),然后朴素树上背包即可,注意本题选择的一部分必须相连。
复杂度大概是 \(O((n^3 + nV)\log n)\)。
code
CF2040C
给定一个长度为 \(n\) 的整数排列 \(p_1, p_2, \ldots, p_n\),其中包含从 \(1\) 到 \(n\) 的所有整数。我们定义一个如下的和式:
\[S(p) = \sum_{1 \le l \le r \le n} \min(p_l, p_{l+1}, \ldots, p_r) \]我们希望找出所有能使 \(S(p)\) 最大的排列,并从中按字典序选择第 \(k\) 个。如果这样的排列数量少于 \(k\),则输出 -1。
$ 1 \leq n \leq 2\times 10^5,1 \leq k \leq 10^{12}$。
典型的注意力大题,我们考虑令 \(S(p)\) 如何最大,记 \(l_i\) 为 \(i\) 左侧第一个小于它的数的位置,\(r_i\) 为 \(i\) 右侧第一个小于它的数的位置,则这一个数的贡献为 \(i\times (i - l_i) \times (r_i - i)\),在这样一个排列中,我们肯定要令大数尽可能大,而他的最大贡献也就是 \(i\times (n-i+1)\),总的来看,这样构成的就是一个 \(1\) 到 \(n\) 的排列,这也就是字典序最小的答案,反之从 \(n\) 到 \(1\) 的排列就是最大的答案。
假设我们向空序列中从小到大填数,肯定是把当前这个数放到中间剩余空位的最左/最右才能使结果最优。说一下为什么最右也能最优,我们称最小答案 \(1\) 到 \(n\) 的排列为原始序列,那么向右填数可以认为把原始序列中后面大数的区间向前平移,使得答案不变,所以,只要按照最左最右填数答案就是不会变的。因此,我们需要在每一个数的位置决策放在最左或最右,而最后一个数是不用决策的,所以序列情况数有 \(2^{n-1}\) 种,无解判断 \(k > 2^{n-1}\) 即可。然后分析放左/右的情况变化,假设当前在 \(i\) 位置,那么在这个位置没有填入时情况数是 \(2^{n-i}\) 种,填在左侧之后,显然字典序是要比填右要小的,所以此时他所在的情况数区间就是 \([1,2^{n-i-1}]\),反之,填右的方案就是 \((2^{n-i-1},2^{n-i}]\),类似线段树上决策,每次向右走减去 \(2^{n-i-1}\) 即可。
Submisson
CF2013E
给定一个序列,可以将序列重新排列,使得 \(\sum_{k=1}^n \gcd(a_1,a_2,\dots,a_k)\) 的值最小,求出这个最小值。
\(1 \leq n \leq 10^5\)。
没切掉 *2200,想了一会感觉没问题交上去 WA on #3,发现考虑少了。
最开始考虑的是 \(\gcd(a_1,a_2,...,a_n)\) 肯定是最小的,于是要尽快的令前缀 \(\gcd\) 达到这个最小值,再进一步就很好想到把最小值放到第一个,考虑这个结论为什么是对的,假设此时的 \(\gcd(a_1,a_2,..,a_n) = 1\),换成其他值也是同理的,此时有 \(a < b <c\),我们先证明 \(a + \gcd(a,b) \leq b\),首先有 \(\gcd(a,b) = \gcd(a,b-a) = \gcd(b,b-a) \leq (b-a)\),所以 \(a + \gcd(a,b) \leq a+(b-a) \leq b\),结论得证,此时我们假设 \(\gcd(b,c) = 1\),有 \(a+\gcd(a,b) < b + \gcd(b,c)\) 和 \(a+\gcd(a,b) < b + \gcd(a,b)\),所以把最小值放在第一个一定会使总值最小。
之后我们要选择第二个值,其实可以把 \(2\) 到 \(n\) 位置的数全部变成 \(\gcd(a_1,a_i)\) 即可,然后把此时的最小值放到第二个,证明同上,一定会使总值最小,所以依次这样放即可,可是时间复杂度看起来貌似是 \(O(n^2)\) 的。
再仔细分析,令全部数 \(\gcd\) 为 \(k\),前缀 \(\gcd\) 最小值为 \(a\),当 \(a \neq k\),后面的数每次取 \(\gcd\) 时,值必定会减少至少一半,因为 \(a\) 最小的因数为 \(2\),除非是 \(a_i = a\),而这样的情况会被放到序列最后,不会被纳入决策,或者令后面一段全部都是 \(a_i =a\),那就不符合 \(a\neq k\) 的前提了,所以每个数至少被减少一半,复杂度实际为 \(O(n\log V)\)。
Submisson
P11126
给定一个值域在 \([1,m]\) 的数组 \(a\),可以将三个相同数或三个连续数化为一组,求分组方案数。
\(1 \leq n,m \leq 5000\),\(n\) 是 \(3\) 的倍数。
显然是放到值域上 dp,由于一个点的方案只跟上一个和上上个有关,所以我们可以设计 \(f_{i,j,k}\) 表示第 \(i\) 个位置 \(i-1\) 剩了 \(j\) 个,\(k-1\) 剩了 \(k\) 个的方案数。
令 \(c_i\) 表示 \(i\) 的数量,把两个分组方式分开转移,我们钦定每一次的 \(k\) 不需要再进行自减,那么有三个数的分组方式 \(f_{i+1,c_i-k,j-k} \gets f_{i,j,k}\),自减的就只有 \(j\),发现这样也满足了前面的假定,\(f_{i,j-3,k} \gets f_{i,j,k}\),就基本做完了,这样复杂度是 \(O(m^3)\) 吗?分析一下,我们令 \(c'_i \gets \max(c_i,c_{i-1})\),那么 \(\sum c_i c_{i-1} \leq \sum {c'_i} ^2 \leq (\sum c'_i)^2 \leq 4m^2\),所以复杂度是正确的,\(O(m^2)\)。
code
P4215
给定一个序列和 \(m\) 个区间 \([l_i,r_i]\),支持两个操作:
- 对某一个数单点减一,保证权值时刻非负
- 查询当前有多少区间内的数值和为 \(0\)。
\(1 \leq n,m \leq 2\times 10^5\)。
好题,我们考虑类似李超树的分割方式,将所有答案区间放到线段树上,记录每个区间的拆分个数,做单点修改时向上 pushup 判断这个区间是否已空,如果已空,那么清除这个区间的所有答案下标即可,具体实现就是线段树每个节点开 vector
,复杂度 \(O(n\log n + m \log n)\)。
code
CF2063E
定义函数 \(f(u,v)\) 表示以 \(dis(lca(u,v),u)\),\(dis(lca(u,v),v)\),\(x\) 为边长构成合法三角形的 \(x\) 取值数量,求 \(\sum_{i=1}^n \sum_{j=i}^n f_{i,j}\)。
\(1 \leq n \leq 3\times 10^5\)。
草,分析对了大部分手贱拆了个式子做不了了。
考虑拆式子,\(|dist(u,lca) - dist(v,lca)| < k < dist(u,lca)+dist(v,lca)\),就是求 \(k\) 的取值数量,即 \(dist(u,lca)+dist(v,lca)-|dist(u,lca) - dist(v,lca)| - 1 = d_u + d_v + 2d_{lca} - |d_u - d_v| -1\),分成三个部分分析,最后一个 \(-1\) 显然有 \(n\times (n-1)\) 个,\(|d_u - d_v|\) 将其看做一个数列问题,按深度排序之后算正负贡献,\(d_u - d_v + 2d_{lca}\) 前面两项的数量显然是 \(n\times (n-1)\),\(lca\) 的数量就是普通的子树计数,注意当 \(lca\) 与 \(u,v\) 重合时 \(-1\) 的贡献不存在,加回来就好了。
Submission
等我再研究一下启发式合并做法。
P11829
给一个图的节点染三个颜色中的一个,一个边还存在当且仅当它两端的节点颜色不同,要求新图中无环,求分组方案。
$ 3 \leq n \leq 2\times 10^5, 0 \leq m \leq 2n-4$。
构造还是做的太少了,开刷蓝题吧。
由性质入手,考虑当图中存在至少三个连通块时做法显然,而对于两个连通块,一个点数少的用一个颜色,另外剩一个大的随便取一个点就好了。
当图中只有一个连通块时,发现题目特别说明了只有 \(2n-4\) 条边,注意到生成树有 \(n-1\) 条边,在树上,我们先把所有点染成一个色,每一条额外边连接的两个点必须是不同颜色的,那么把每条边两个端点合并,因为只有 \(n-3\) 条额外边,所有会剩下 \(3\) 个集合没有合并,这三个集合染三个颜色就做完了,好构造。
code
P11818
交互,至多询问 \(\left \lfloor \frac{5n}{2}\right \rfloor\) 次,一个 \([0,n-1]\) 的排列,定义 \(\gcd(0,a) = a\),每次可以询问 \(\gcd(i,j)\),找出 \(1\) 的位置。
$3 \leq n \leq 5\times 10^5 $。
头一回写交互,这题倒是不难。
考虑到 \(0\) 的特殊性质,只要找到了 \(0\) 那么我们自然可以逐个找到 \(1\),我们先随机抓一个数出来,把他跟所有数询问一遍,显然,如果当前数是 \(1\),那么答案全是 \(1\),如果是 \(0\),那么答案是 \([1,n-1]\) 的排列,这两种直接判掉就好了。
否则,得到的答案中最大的数就是当前数的原始值(因为存在 \(0\)),我们把答案为最大数的位置都取出来,显然他们要么是 \(0\) 要么是这个数的倍数,对这些数再重复上述操作,能够发现,取出来的数可能为 \(0\),那么操作完只会剩下 \(0\) 和另一个最大数,反过来,如果我们取了最大值,那么剩下的也会是 \(0\) 和最大值,所以我们并不能确定 \(0\) 的位置,每一次也不能删除我们一开始取来使用的数字。
经过很多轮这样的做法后,我们就会剩下不超过 \(2\) 个数,剩下一个的话显然就是 \(0\),对于剩 \(2\) 个数,我们并不关系哪个是 \(0\),只要找 \(1\) 就好了,将他们与我们第一次操作剔除的数进行询问,如果与第一个数得到的答案不是 \(1\),那么当前数就肯定不是 \(1\),否则再与第二个数询问,如果为 \(1\),那么当前数显然为 \(1\)(因为 \(0\)),否则第二个数就肯定是 \(0\),直接拿第二个数继续做就好了。
分析一下询问次数,最开始我们需要 \(n\) 次询问,后面每一次的最坏情况是询问到 \(2\) 的倍数,每次只能剔除一半的数,最后一步进行的询问数就是最开始剔除的数的个数,也就是 \(\left \lceil \frac{n}{2} \right\rceil\) 次,所以最坏询问次数 \(\left \lceil \frac{n}{2}\right \rceil + \sum_{i=0}^{\log_2 n} \left \lfloor \frac{n}{2^i} \right \rfloor \leq \left\lceil \frac{5n}{2}\right \rceil\),可以通过。
code
P10281
给定 \(n\) 个区间 \([l_i,r_i,k_i]\),一个区间能产生的贡献数是与他相交超过 \(k_i\) 的区间个数,求总贡献。
\(2 \leq n \leq 2\times 10^5 , 0 < l_i \leq r_i \leq 10^9\)。
NOIP T4 后半部分的原题。
写出对答案有贡献的区间 \([l_j,r_j]\) 的条件,即 \(\min(r_i,r_j) - max(l_i,l_j) \geq k_i\),其等价于,对于一个 \(r_j - l_j \geq k_i\) 的区间,满足 \(l_j \leq r_i - k_i \wedge r_j \geq l_i + k_i\),发现这样可能会存在重复统计,正难则反,考虑计算 \(l_j > r_i - k_i \vee r_j < l_i + k_i\),且满足 \(r_j - l_j \geq k_i\) 的区间,一减就好了,前后两个判定显然是相互独立的,所以可以分开计数,因为值域到 \(10^9\),所以我们选择动态开点线段树,分别维护 \(l_j\) 和 \(r_j\),然后将区间分别按 \(k_i\) 和 \(r_j - l_j\) 倒序排就做完了。
代码很短,因为标记永久化的动态开点线段树超级好写。
code
Black Nodes in Subgraphs
多测,一棵树每个点有颜色,\(q\) 次询问,每次给定 \(s,d\),询问能否找到大小恰好为 \(s\) 的连通子图恰好有 \(d\) 个黑点。
\(T \leq 5,n \leq 5000,q \leq 10^5,0 \leq d \leq s,1 \leq s \leq n\)。
codechef 题,放的是 vj 链接。
首先有一个 \(O(\frac{Tn^3}{w})\) 的 bitset 做法,令 \(f_{u,i,j}\) 表示以 \(u\) 为根,且这个根必选的恰好 \(i\) 个点能否有 \(j\) 个黑点,转移显然是 \(O(n^2)\),再用 bitset,但是这个复杂度并不能通过。
让我们重新考虑,讲判定性问题转化为最值问题,令 \(f_{i,s}\) 表示以 \(i\) 为根的子树内,选 \(s\) 个点的最多黑点个数,同理 \(g_{i,s}\) 表示最少黑点个数,可以通过简单调整发现可取的黑点个数一定是 \([g_{i,s},f_{i,s}]\),然后可以拿 bit 维护一下区间,每次查询就可以做到 \(O(Tn^2\log n)\) 的复杂度。
code
CF1312E
一个数列,每次可以合并相邻两个相同项,设这两项的值为 \(x\),则用 \(x+1\) 替换之后放到原位置,求最终最小序列长度。
\(n \leq 10^5,a_i \leq 10^6\)。
显然区间 dp,直接 dp 一个区间的左右端点并不好做,不如转换一下,由于最终序列每个数字都不同,我们可以将其看作一段段数字,而且是连续的一段,所以我们直接令 \(f_{i,j}\) 表示区间 \([i,j]\) 中可以合并成一个数字 \(f_{i,j}\),然后直接 dp 就好了,遇到一个区间合并不完的,就不合法,不管了。
考虑下一步,此时我们只要用最少的段数去覆盖整个数列就好了,这个也是 dp,令 \(g_i\) 表示到 \(i\) 位置的最小步数,则有转移 \(f_i = \max_{j<i \wedge f_{j,i-1} \neq 0} f_j + 1\),直接做是 \(O(n^2)\) 的,完全可以通过,总复杂度 \(O(n^3)\)。
Submission
P10283
给定 \(n\) 个 01 字符串,一次操作可以在某个字符串末尾加一个 \(0\) 或 \(1\),求所有字符串互不为前缀的最小操作次数。
\(1 \leq n \leq 10^5\)。
trie 树上贪心 + 启发式合并。
把所有串扔到 trie 树上,最终结果是每一个串的结尾都在叶子上,每次加 \(01\) 都是将这个点下放到儿子,对于一个点 \(x\),假设令他走到已存在某叶子 \(y\),并且令其变为 \(y\) 的儿子,那么移动次数是 \(dep_y - dep_x + 1\),如果 \(y\) 点上也有某个字符串,因为不能是前缀,所以这个串也要下方到另一个儿子上,故此时的移动次数是 \(dep_y - dep_x + 2\),假设走到的 \(y\) 不是叶子,但是有儿子为空,那么我们显然也可以把 \(x\) 放到这个空的儿子上,我们默认此时 \(y\) 上没有字符串,那么我们就省去了刚才移动另一个串的次数,所以操作次数是 \(dep_y - dep_x +1\) 的。
考虑上述过程,其实就是在 \(x\) 的子树内找一个深度最小的 \(y\) 使得 \(dep_y - dep_x + 1/2\) 最小,这个可以用 set
维护,然后返回父节点时启发式合并,注意记录一下如果走到这个点的叶子是否需要额外动点就好了,复杂度可以发现这样做最坏情况是满二叉树,所以深度不超过 \(\log n\),总复杂度 \(O(n \log^2 n)\),可以用可并堆做到单 \(\log\)。
code
P11363
请自行阅读题面。
NOIP2024 T3,题面又臭又长,真是不会写了。
突然想起来补这个,因为 T4 那个维护极长连续段太难写了。
考虑当 \(k=1\) 时做法显然,虽然场上好像想了很久很久,发现以起始边的两端为端点,然后记 \(\prod (d_i - 1)!\) 即可,\(d_i\) 是无向图的度。
特判掉 \(n=2\),当 \(k>1\) 时,我们的答案错误主要来源于不同起始边的重复计数,考虑在什么情况下会导致多个不同起始边能够走出相同的树形状。
啊啊啊,看第一篇题解的图吧,那个确实很好理解,从菊花图开始考虑,假设一个点连了其他所有点,那么他的树形状显然就是一条链,这条链虽然也会被重复计数,但只有链两头的边会重复计数。由此,我们能够发现,对于一个点周围的黑边,他一定会形成一条链,而只有链两头可以进出,也可以理解为只有链两头及以外的边可以作为起始边。
哎,我们此时就可以猜结论了,其实这结论也挺简单的,一个新建树可能被构造出来的根节点一定是原树上的一条叶子到叶子的边,因为我们默认这是个无根树,所以就叫做叶子到叶子了。证明其实也就是我们上面提到的那个性质,进出点固定的话只有一条边能被一个点顺下来,每个点都这样也就形成了原树的一条链。
有了这个性质我们就好做了,此时我们只要除去链上每次多余数的就好了,也就是 \(\prod (d_i - 1)! \prod (d_v - 1)^{-1}\),其中点 \(v\) 是链上的点,其实就是去掉了链上点多余数的,因为固定了这一条链上面的点咋数都是一个形状。
然后我们考虑套到题目中,因为有关键边的限制,我们不妨将是否关键边当做边权,令 \(f_{i,0/1}\),表示 \(i\) 为根的子树内叶子到 \(i\) 的点积和,以及是否经过了至少一条关键边,因为我们这个方案是去重过的,所以只要上面有一条关键边数一遍就好了,对于转移就很自然了,记得对叶子特判,和对每个点都要乘上点积。记得预处理逆元。
code