2023 省选做题记录 3.0
新学期就要开新坑(确信)。
2.5
元宵节!
[ABC288E] Wish List(DP)
如果前 \(i-1\) 个物品里买了 \(j-1\) 个,那么第 \(i\) 个物品如果购买就可以作为编号第 \(i-j+1\sim i\) 小的物品,附加代价就是 \(\min\limits_{k=i-j+1}^i c_k\)。
然后直接 DP 即可。
代码:https://paste.ubuntu.com/p/GjjfJySgNw/。
[CF1784A] Monsters (easy version)(模拟)
容易发现把第二种操作放到最后做不劣。
维护最后第二种操作的操作次数 \(x\),那么之前肯定所有数都已经在 \([1,x]\) 中且 \([1,x]\) 中每一个数都存在。
从小到大枚举 \(x\),如果这一位有值就 skip,否则依次尝试通过把某个数缩小来填充 \(x\) 这个空位。优先队列模拟即可。
代码:https://paste.ubuntu.com/p/7wY5JBw5Q8/。
[CF1784B] Letter Exchange(构造 + 模拟)
垃圾题。但还是想了一段时间。菜是原罪。
考虑到两个字母的情况非常好处理,于是尝试先通过分配 w 来使得每一个人都有恰好一个 w,再就转化成了两个字母的情况。
单独拿出来 www、wwi、wwn 的集合和 iin / iii 与 nni / nnn 的集合。
对于有两个以上 i 的情况,先尝试与 wwn 交换,再尝试与 www 交换,最后再与 wwi 交换。
有两个以上 n 的情况同理。
最后只需要 wii 和 wnn 配对交换即可。
代码实现及其恶心。
代码:https://paste.ubuntu.com/p/KGTkJJ9qD8/。
2.6
开学了!
[CF1784C] Monsters (hard version)(线段树 + 树状数组)
和 CF1785A 一样的想法,也是填充一段前缀。称填充这段前缀的数的集合为合法集合。
考虑在新加入一个数 \(a_i\) 的时候,如果之前合法集合里 \(\le a_i\) 的数已经有了 \(a_i\) 个,那么它可以被忽略。这个可以用树状数组统计。
否则就尝试给这段前缀长度 \(+1\)。如果依然合法,即对合法集合里的数排序之后(设为 \(b\) 数组),\(\forall i,b_i-i\ge 0\)。可以用线段树维护。如果不合法,就要把不合法的位置对应的位置的数删掉,代价为 \(pos-a_i\)(可以理解为 \(a_i\) 去补之前 \(pos\) 位置上的数的位置,因为这样更优)。
代码:https://paste.ubuntu.com/p/YzqDFcHhVp/。
[CF1784D] Wooden Spoon(DP)
先把题目条件反过来,假设是编号大的胜利,输出的时候再反过来输出就可以了。
设 \(f_{i,j}\) 表示 \(j\) 已经是 \(2^i\) 个数中最大的(即他赢了之前所有比赛),下一轮 \(j\) 输掉的方案数。
考虑自顶向下 DP。边界 \(f_{n,2^n}=1\)。
枚举 \(j\) 下一轮输给了 \(k\),那么方案数就是 \(f_{i+1,k}\times\binom{k-2^{i}-1}{2^i-1}\times (2^i)!\)。把这些都加起来,最后 \(\times 2\) 就是 \(f_{i,j}\) 的值(\(\times 2\) 是因为 \(j\) 和 \(k\) 有左右顺序)。
代码:https://paste.ubuntu.com/p/P6H7C4Q8HS/。
参考 AW's sol:https://www.luogu.com.cn/blog/AlexWei/solution-cf1784d。
[洛谷 P9036]「KDOI-04」挑战 NPC III(搜索 + 性质)
看到这个 \(k\) 很小,考虑一些暴力的东西。
注意到独立集取反之后就是一个点覆盖。所以考虑求出所有大小为 \(k\) 的点覆盖的方案数。
显然如果一个点度数 \(>k\) 它就一定要被选。因为如果不选那么所有和它相连的点都要选,这样选的点数就 \(\ge k\) 了,不合法。一直做下去可以使得图中所有点度数都 \(\le k\)。
接下来考虑爆搜。枚举每个点是否被选。容易发现无论它是否被选,只要它的度数 \(\ge 1\),每一轮之后 \(k\) 就至少都会减去 \(1\)。因此搜索最多只会进行 \(k\) 轮。
发现条件跟度数有关,因此考虑每次枚举度数最大的点是否被选。这个可以用 set 维护。每次只会有 \(\mathcal{O}(k)\) 个点受到影响,因此复杂度是 \(\mathcal{O}(2^kk\log n)\) 的。
如果所有点度数都是 \(0\),那么剩下的点可以随便选,方案数是一个组合数。
注意一些实现细节:
- 把重边都删掉。因为我们的算法都和度数有关。
- 删完点后回溯加回来要倒序。因为这是撤销操作,只能倒过来。
代码:https://paste.ubuntu.com/p/JqVcpzsj38/。
[ARC097F] Monochrome Cat(性质 + 思维)
注意到黑色的叶子是肯定不会经过和操作的。所以可以通过拓扑排序不断删去黑色的叶子。接下来得到的新树的所有叶子都是白色的。
先考虑如果我们走的是一条回路,那么一个点就会翻转度数次。所以如果一个点度数为奇数就会被翻转颜色。
我们把所有翻转之后是白色的节点打上标记。
考虑把回路变成路径的影响,就是一条路径 \(u\to v\) 上的所有点翻转颜色,并且少经过了路径长度条边。如果某个点本来不要翻转现在要翻转,那么它的贡献就是 \(0\),否则就是 \(2\)。
即:
- 对于没被标记的点,少经过一次但同时要多翻转一次颜色。
- 对于标记了的点,少经过一次还少翻转一次颜色。
所以我们要找到一条路径,经过尽可能多的被标记了的点。
这其实就是树的直径。
最后一个问题,这样 \(u\) 点本来不应该统计进去,答案不会多吗?
并不会!因为叶子节点的权值必定是 \(0\),一定存在不包含叶子节点的直径,然后我们取叶子节点作为 \(u\)。
最后树形 DP 求直径,记得这里一定要找到一个没被删掉的点当根,再跑。
答案为删完剩余边数乘二加标记的点数减去直径长度。
代码:https://paste.ubuntu.com/p/JyyzBmqX9V/。
[CF1283F] DIY Garland(构造 + 优先队列)
mmp,*2200 都不会。sb。陷入一个误区就出不来了怎么办啊啊啊???
注意到每个点的出现次数就是儿子个数。所以一开始可以求出所有叶子。
从后往前连边,每次找到编号最小的叶子,向当前点连边,并且将当前点的儿子个数减去 \(1\),如果它又变成了叶子就将它放入优先队列中。
这种剥叶子的思路太典了。
代码:https://paste.ubuntu.com/p/pw44brN8gM/。
2.7
[ZJOI2022] 众数(根号分治)
不会根号/ll
题意可以转化为:选择一段区间,最大化 区间内众数出现次数 加上 整个序列去掉这个区间后的众数出现次数 之和。
众数就一眼不能 \(\rm{polylog}\) 的样子,而且又有出现次数,于是考虑根号分治。将数字按照出现次数 \(>\sqrt n/\le \sqrt n\) 分类。
- 出现次数 \(>\sqrt n\) 的数字只有 \(\mathcal{O}(\sqrt n)\) 个,所有颜色种类数是 \(\mathcal{O}(n)\) 的,对每种颜色处理就是 \(\mathcal{O}(n\sqrt n)\) 的。
- 对于出现次数 \(\le \sqrt n\) 的数字,我们把它出现的位置拿出来:\(p_0=0,p_1,\dots,p_k,p_{k+1}=n+1\),显然改的区间为 \((p_i,p_j)\) 最优,枚举每个数字和每段区间就是 \(\mathcal{O}(\sqrt n)\) 的了。
然后先处理出现次数大于 \(\sqrt n\) 的。假设这种颜色为 \(p\),我们试图把 \(p\) 改成 \(q\) 更新 \(q\),或是把 \(q\) 改成 \(p\) 更新 \(p\)。维护 \(p\) 出现次数的前缀和直接处理,这是简单的。
然后再处理出现次数不大于 \(\sqrt n\) 的数。注意到一点是在上面的处理中我们处理了一个出现次数大于 \(\sqrt n\) 的数 \(p\) 改成出现次数不大于 \(\sqrt n\) 的数 \(q\),所以在这里只考虑两个数出现次数都不大于 \(\sqrt n\) 的。
那么相当于做 \(\mathcal{O}(n\sqrt n)\) 次区间众数。但是有一点是每个数出现次数都是不大于 \(\mathcal{O}(n)\) 的。那么记 \(s_i\) 为 \([i,n]\) 的众数出现次数,容易发现 \(\sum s_i=\mathcal{O}(n\sqrt n)\) 的,其支持暴力维护。
暴力枚举 \([l,r]\)。现在已经枚举了 \(r\),再枚举 \(l\)。容易发现我们可以根据上面分讨得出的一个简单小结论优化枚举过程,也就是保证 \(l-1\) 和 \(r\) 颜色一样,或者 \(l=1\) 即可。显然 \(s_i\ge s_{i-1}\),我们可以暴力更新 \([i,r]\) 的区间众数,更新次数不大于 \(\mathcal{O}(n\sqrt n)\),可以保证复杂度。
Copied from https://www.luogu.com.cn/blog/blog10086001/solution-p8330。Orz SH。
代码:https://paste.ubuntu.com/p/mv7MBz3s56/。
2023.2.7 考试 T2 随机 (random)(trie 树合并 + 树上高斯消元)
题面:
有一棵 \(n\) 个点的树和 \(m\) 个操作
x,y,S,表示给 \(x\) 到 \(y\) 的链上的节点都加入一个数字字符串 \(S\)。所有操作都结束后需要对每个点进行一次询问。首先在该节点的所有字符串形成的 trie 上随机选择一个点(此时为步数 \(0\)),这里定义 trie 的根节点为空串,然后每次随机一个与当前点相邻的点移动过去且步数 \(+1\),如果移动到了某个字符串对应的节点则结束。
询问每个点对应 trie 的期望步数。
输出模 \(998244353\) 意义下的值。
数据范围:\(1\le n,m\le 3\times 10^5\),\(1\le |S|\le 10^6\)。
树上高斯消元是显然的。需要再维护子树内期望步数和。
先把所有串插入 trie 树中,树上差分,dfs 的时候 trie 树上保留当前点拥有的字符串。维护每个节点被经过的次数和作为结尾结点的次数。每次相当于修改一条链。
参考:https://www.cnblogs.com/Jessie-/p/10633036.html。
代码:https://paste.ubuntu.com/p/vrf8W2GXyF/。
[ARC118E] Avoid Permutations(容斥 + DP)
如果 \(P\) 已知,那么有一种显然的 \(\mathcal{O}(n^2)\) DP 做法:设 \(f_{i,j}\) 表示走到 \((i,j)\) 的方案数。遇到障碍直接不转移。
现在考虑 \(P\) 有空缺的情况。直接枚举排列没有什么优化的空间,考虑对于一条路径计算有多少合法的排列,即路径不经过排列上的点。
“所有点都不经过”是困难的,考虑容斥,钦定有 \(k\) 个点被经过,容斥系数就是 \((-1)^k\)。
值得注意的是,对于已经确定的 \(p_i\),它带来的限制是“第 \(i\) 行和第 \(p_i\) 列都不能有障碍”以及 \((i,p_i)\) 不能被经过。也就是说,我们钦定的都是 \(p_i=-1\) 的位置,对于已经确定的 \(p_i\),我们只是当作一个已经确定的不能走的障碍,不在容斥里考虑。
假设一共有 \(cnt\) 个位置满足 \(p_i=-1\)。
设 \(f_{i,j,k,0/1,0/1}\) 表示走到了 \((i,j)\),已经经过了 \(k\) 个障碍,且第 \(i\) 行 / 第 \(j\) 列是否已经有了障碍的方案数。
转移直接钦定 \((i,j)\) 是否放了障碍。容斥系数在转移的时候一并算入。
最终答案是 \(\sum\limits_{i=0}^{cnt}f_{n+1,n+1,i,0,0}(cnt-i)!\)。
代码:https://paste.ubuntu.com/p/gfKW6Z3wrF/。
[CF1311E] Construct the Binary Tree(构造 + 贪心)
好不容易想出来,结果你告诉我只有 *2200……为什么是紫的啊???
先考虑算出 \(n\) 个节点的二叉树深度之和的最小值和最大值,也就是完全二叉树和链的情况。先判掉 \(d\) 不属于这个区间的情况,即无解。
接下来通过调整证明在这个区间内的所有值都可以达到。
设 \(cnt_i\) 表示深度为 \(i\) 的点的数量。一个 \(cnt\) 序列可以被一棵二叉树表示当且仅当 \(\forall i,2cnt_i\ge cnt_{i+1}\)。
我们可以每次找到一个叶子并把它的深度不断加一,直到深度之和满足条件或者不能再增加它的深度。
容易发现调整到最后的情况就是一条链。
代码:https://paste.ubuntu.com/p/jjDyWcDZZn/。
[AGC011C] Squared Graph(性质 + 二分图)
拜谢 hzk。
先考虑图连通的情况。
如果存在一条长度为 \(l\) 的 \(a\to b\) 和 \(c\to d\) 的路径,那么 \((a,c)\) 与 \((b,d)\) 连通。
因为是无向图,所以我们可以轻易地将路径长度加上 \(2\),因此只要考虑路径长度的奇偶性。这是无向图上非常重要的思想,如果在理论层面出现了“路径长度”,容易将其转化为奇偶性,然后使用二分图相关理论解决问题。

代码:https://paste.ubuntu.com/p/cJPJGXxBhZ/。
[CF1783E] Game of the Year(差分 + 枚举倍数)
题意即求出所有 \(k\) 满足 \(\forall 1\le i\le n,\lceil\frac{a_i}{k}\rceil\le \lceil\frac{b_i}{k}\rceil\)。
显然如果 \(a_i\le b_i\) 那么一定会满足条件。如果 \(a_i>b_i\),那么条件等价于 \([b_i,a_i-1]\) 中没有 \(k\) 的倍数出现。
这是一个线段覆盖,可以差分做。
枚举 \(k\) 的所有倍数即可。
代码:https://paste.ubuntu.com/p/7wrmHb85BT/。
2.8
2023.2.8 考试 T1 三叉戟 (trident)(构造 + 扩展域并查集)
题意:
给定一张 \(n\) 个点 \(m\) 条边的简单无向图,一开始每条边有一个黑色或白色的颜色,你需要通过操作把所有边变成白色的。
我们称三条 不同的 有公共点的边组成的结构为三叉戟(形如 \((a, x), (a, y), (a, z)\))。每次你可以选择一个三叉戟,将三叉戟内的边反色,即将这三条边,黑的变成白的,白的变成黑的。
你需要通过不超过 \(10m\) 次操作,将所有边变成白色的。输出任意一个方案,或输出无解。
数据范围:\(n\le 2\times 10^5,m\le 10^5\)。
度数为 \(1/2\) 的点显然不需要考虑。因为它们无论如何都不能进行操作。
对于度数 \(\ge 4\) 的点,我们可以通过 \(3\) 次操作来将一条边反色并且不影响其它边的颜色:
- 设与该点相连的边中有四条边 \(a,b,c,d\),其中 \(a\) 需要反色。
- 进行操作 \((a,b,c),(a,b,d),(a,c,d)\)。
那么我们只要考虑度数为 \(3\) 的点,设 \(x_i=0/1\) 表示三度点 \(i\) 的出边是否反转颜色。
考虑一条边,设其端点为 \(u\) 和 \(v\)。
- 如果它两端点的度数都 \(\le 2\) 并且它是黑色,那么显然无解。
- 如果其中有一端度数为 \(3\),另一端度数 \(\le 2\),那么就相当于钦定这个三度点是否被选(依赖于这条边的颜色),即钦定 \(x_u=0/1\)。
- 如果两个端点度数都为 \(3\),那么相当于要求 \(x_u=x_v/x_u\ne x_v\) 。
可以用扩展域并查集维护。对于钦定 \(x_u=0/1\),可以在它对应的连通块上打 tag,表示这个连通块必选。无解的判定是简单的。
代码:https://paste.ubuntu.com/p/RrPjyGwdd6/。
[AGC009C] Division into Two(DP + 前缀和优化 + 二分)
难得做出 AtCoder 的 2400 啊……
显然最终序列一定是 AB 交替的。
设 \(f_{i,0/1}\) 表示将 \(1\sim i\) 划分成若干段,\(i\) 是当前段的结尾,并且 \(i\) 属于集合 \(X/Y\) 的方案数。
考虑从 \(f_{j,1}\) 转移到 \(f_{i,0}\) 需要满足的条件:
- \(\forall j+2\le k\le i,a_k-a_{k-1}\ge A\);
- \(a_{i+1}-a_j\ge B\)。
从 \(f_{j,0}\) 转移到 \(f_{i,1}\) 的条件同理。
显然所有满足条件的 \(j\) 是一个区间。第一个条件可以对 \([a_k-a_{k-1}\ge A]\) 做一个前缀和再二分,第二个条件可以直接二分。为了减少边界,可以令 \(a_0=-\infty\) 和 \(a_{n+1}=\infty\)。
最后直接前缀和优化 DP 即可。
代码:https://paste.ubuntu.com/p/w5P5wM9Sk8/。
emm……看了题解发现可以直接双指针做到线性……
2.9
[ARC116E] Spread of Information(二分 + 树形 DP)
第一眼肯定是二分。接下来问题就转化为:选出 \(K\) 个关键点,使得每个点到最近的关键点的距离 \(\le mid\)。
贪心地自底向上覆盖肯定是正确的。接下来就考虑怎么维护这个过程。
设 \(f_i\) 表示 \(i\) 到子树内最近的关键点的距离,\(g_i\) 表示 \(i\) 到未被关键点覆盖到的点的最远距离。
如果 \(f_i+g_i\le mid\) 就说明 \(i\) 如果不是关键点,\(i\) 的子树也可以满足条件。
如果 \(g_i=mid\),那么 \(i\) 就必须被选为关键点,不然会不符合条件。
代码:https://paste.ubuntu.com/p/MBq8mWg7gh/。
[ZJOI2016] 小星星(树形 DP + 容斥)
一个显然的想法是在树上进行 DP。设 \(f_{i,j,S}\) 表示 \(i\) 的子树内,\(i\) 对应的点为 \(j\),\(i\) 的子树内的所有点对应的集合为 \(S\) 的方案数。转移枚举 \(i\) 的子结点以及它对应的点和对应子树内集合。时间复杂度 \(\mathcal{O}(n^3\times 3^n)\)。
瓶颈在于枚举子集。思考一下用什么方法可以规避掉枚举子集的时间?
尝试容斥。枚举所有数对应的点都在 \(S'\) 中出现,那么就可以直接设 \(f_{i,j}\) 表示 \(i\) 对应的点为 \(j\) 时 \(i\) 子树内的答案。转移直接枚举子结点对应的点是什么。
答案就是 \(|S'|=n\) 的答案 减去 \(|S'|=n-1\) 的答案 加上 \(|S'|=n-2\) 的答案……
代码:https://paste.ubuntu.com/p/nG8qPTYq8g/。
[CF1779F] Xorcerer's Stones(树形 DP + 构造方案)
就这 *2500??????
因为值域很小,所以考虑将其设入 DP 状态中。
设 \(f_{i,j}\) 表示 \(i\) 子树异或和能否为 \(j\)。转移就是一个树形背包。注意当 \(sz_i\) 为偶数的时候我们可以直接操作一次 \(i\) 使得 \(i\) 子树异或和为 \(0\)。
对于输出方案,考虑在转移的过程中记录每个子树的异或和,执行操作的地方要打上标记。
代码:https://paste.ubuntu.com/p/HcqHwtrm85/。
Codeforces Round #784 (Div. 4)(CF1669)A~H
[AGC020C] Median Sum(性质 + bitset 优化背包)
设 \(sum=\sum\limits_{i=1}^n a_i\)。
因为是子集和,所以如果有一个子集和为 \(x\),那么肯定就有一个和它互补的子集和为 \(sum-x\)。也就是说它们是对称的。
所以只需要考虑 \(\ge\lceil\frac{sum}{2}\rceil\) 的所有数中最小的一个可以被若干个数的和表示出来的数。
可以使用 bitset 优化 01 背包。
代码:https://paste.ubuntu.com/p/wwDpnqSBsw/。
2.10
[CF1788E] Sum Over Zero(线段树优化 DP)
显然的 \(\mathcal{O}(n^2)\) DP:设 \(f_i\) 表示 \(1\sim i\) 中选择若干个区间的答案。显然 \(f_i\) 可以直接继承 \(f_{i-1}\)。如果 \(j\sim i\) 的和 \(\ge 0\) 就 \(f_i\leftarrow\max(f_i,f_{j-1}+i-(j-1))\)。
将条件改写一下,记前缀和 \(s_i=\sum\limits_{j=1}^i a_j\),那么条件就是 \(s_i-s_{j-1}\ge 0\iff s_i\ge s_{j-1}\)。
将所有 \(s_i\) 离散化,建一棵线段树,下标为 \(s_i\),存的值是所有前缀和等于 \(s_i\) 的 \(i\) 中最大的 \(f_i-i\)。转移直接前缀 \(\max\) 就行。
代码:https://paste.ubuntu.com/p/tWXxKvPhwQ/。
[CF1788F] XOR, Tree, and Queries(构造 + DFS + 性质)
显然条件可以转化成:设 \(ans_i\) 为 \(i\) 到根的路径上的所有边的异或和,那么 \(ans_u\oplus ans_v=w\)。
抽象一下,建一张新图,对于这样的条件连边 \((u,v,w)\),相当于限制 \(ans_u\oplus ans_v=w\)。在每一个连通块里随便钦定一个点,令它的 \(ans\) 为 \(0\),从它开始 DFS,如果有一条边不能满足限制说明无解。可以用类似二分图染色的思想去理解。因为显然同一个连通块的所有 \(ans\) 全部异或上某个值肯定是没有影响的。
现在我们已经得到了一组可行解,接下来考虑最小化所有边权异或和。
注意到每个 \(ans\) 是否会对全局异或和产生贡献取决于这个点的度数,如果它度数为奇数就可以对全局异或和进行贡献。
所以设现在可行解得到的全局异或和为 \(x\),找到一个 内部有奇数个会对全局异或和产生贡献的点 的连通块,将其内部所有 \(ans\) 全部异或 \(x\),这样就可以将全局异或和变成 \(0\) 了。
代码:https://paste.ubuntu.com/p/4gR4mMW7yV/。
[洛谷 P3369]【模板】普通平衡树
我就不写了!
板子:https://paste.ubuntu.com/p/bGWpJxYMrt/。
[洛谷 P3391]【模板】文艺平衡树
区间翻转:https://www.cnblogs.com/Equinox-Flower/p/10785292.html。
板子:https://paste.ubuntu.com/p/xcS3CHyT5h/。
2023.2.8 考试 T2 后缀数组 (sa) 80 分做法(平衡树)
题面:

数据范围:\(n\le 10^9,m\le 10^5\)。
80 分范围:\(n\le 10^5\)。
可以使用文艺平衡树模拟这个操作。复杂度线性对数。
求出来 SA 后,我们考虑同时求出 \(rk\) 数组,即 \(rk_{sa_i}=i\)。
那么 SA 相当于限制了 \(a_{sa_1}\le a_{sa_2}\le a_{sa_3}\le \dots\le a_{sa_n}\)。显然 \(0\le a_{sa_i}-a_{sa_{i-1}}\le 1\),因为字符串必须满足题目中给的那个条件。
假设有 \(cnt\) 个可以取到等号,那么方案就是 \(2^{cnt}\)。
考虑什么时候可以取等号。显然需要满足 \(rk_{sa_i+1}>rk_{sa_{i+1}+1}\)。
所以直接算就是了。
80 分代码:https://paste.ubuntu.com/p/zk2wgtD9gy/。
2.11
2023.2.10 考试 T1 排列 (permutation) & [LOJ3395]「2020-2021 集训队作业」Yet Another Permutation Problem(容斥 + DP)
蒯题解:https://www.cnblogs.com/ycx-akioi/p/solution-loj3395.html。
阴间题。
丢几个代码:
[ARC112E] Cigar Box(DP + 组合计数)
显然每个数只有最后一次操作会影响它最终的位置。
设最后有 \(l\) 个数被提到最前面,\(r\) 个数被提到最后面。那么最终序列需要满足 \(a_{l+1},a_{l+2},\dots,a_{n-r}\) 递增,因为它们都没有被操作。
考虑一个 DP:设 \(f_{i,l,r}\) 表示后 \(i\) 次操作中,有 \(l\) 个数被提前,\(r\) 个数被提后,转移有:
- \(f_{i+1,l,r}\leftarrow f_{i,l,r}\times 2\times (l+r)\),表示这是一次无效操作,对最终序列没有影响;
- \(f_{i+1,l+1,r}\leftarrow f_{i,l,r}\),将一个数提前;
- \(f_{i+1,l,r+1}\leftarrow f_{i,l,r}\),将一个数提后。
这种时间倒流的思想很巧妙。
发现转移只与 \(l+r\) 有关,所以考虑直接记录 \(f_{i,l+r}\),最后再用组合数来分配提前和提后的操作,即系数为 \(l+r\choose l\)。
对于所有合法的 \(l,r\) 统计答案即可。
代码:https://paste.ubuntu.com/p/ckW39y7YQv/。
The 1st Universal Cup. Stage 3: Poland 部分题目
2.12
2023.2.10 考试 T2 Power(power)(数论)
题意:
给定质数 \(P\),定义 \(S(x)\) 为一个长度为 \(P\) 的序列 \(\{x^0\bmod P,x^1\bmod P,x_2\bmod P,\dots,x^{P-1}\bmod P\}\),定义 \(T(x)\) 为 \(S(x)\) 的前缀最大值序列。
\(T\) 组数据,每次给定 \(x\),求 \(T(x)\) 的元素之和,不进行取模。
数据范围:\(T=200\),\(P\le 10^9\),\(P\) 在范围内的质数中独⽴均匀随机,每个 \(x\) 在 \([1,P)\) 中独⽴均匀随机⽣成。
有两种暴力:
- 枚举 \(x_i\bmod P\)。
- 枚举 \(P-i\),求出 \(x^{pos}\bmod P=P-i\)。
如何将这两种暴力拼起来?设置一个阈值 \(B\),对于 \(<B\) 的位置做第一种暴力,否则就一直枚举 \(P-i\),直到其位置比 \(B\) 小。第二种暴力怎么保证时间复杂度?考虑到 \(P\) 是随机的,假设枚举到了 \(P-m\),那么位置大概是在 \(\frac{P}{m}\) 的数量级里。感性理解就行……
第一种暴力是朴素的。
对于第二种暴力,可以每次 BSGS,但是会 TLE。考虑怎么优化。
注意到模数固定,也就是说我们要求形如 \(x^i\equiv y\pmod P\) 的最小整数解,其中 \(P\) 固定。
称满足 \(a^n\equiv 1\pmod m\) 的最小正整数 \(n\) 为 \(a\) 模 \(m\) 的阶,记作 \(\delta_m(a)\)。显然因为欧拉定理,阶一定存在。最重要的性质就是 \(a^0,a^1,\dots,a^{n-1}\) 模 \(m\) 两两不同余。
更多性质详见 https://oi-wiki.org/math/number-theory/primitive-root/。
原根:设 \(m \in \mathbb{N}^{*}\),\(a\in \mathbb{Z}\)。若 \(\gcd(a,m)=1\),且 \(\delta_m(a)=\varphi(m)\),则称 \(a\) 为模 \(m\) 的原根。
原根判定定理:设 \(m \geqslant 3, \gcd(a,m)=1\),则 \(a\) 是模 \(m\) 的原根的充要条件是,对于 \(\varphi(m)\) 的每个素因数 \(p\),都有 \(a^{\frac{\varphi(m)}{p}}\not\equiv 1\pmod m\)。
最小原根数量级是在 \(m^{0.25}\) 级别的,因此可以暴力找原根。
所以我们可以先求出 \(P\) 的原根 \(g\),设 \(x\equiv g^a\pmod P\),\(y=P-i\equiv g^b\pmod P\)。前者可以一遍 BSGS,后者可以直接预处理。我们的目标就是要求出最小的 \(i\) 满足 \(x^i\equiv y\pmod P\),即 \(g^{ia}\equiv g^b\pmod P\)。
因为 \(g^{\varphi(P)}\equiv 1\pmod P\),所以 \(ia\equiv b\pmod {\varphi(P)}\)。也就是一个 exgcd 能够解决的问题了:\(i\times a+j\times \varphi(P)=b\),解出 \(i\) 和 \(j\) 的值就行。
代码:https://paste.ubuntu.com/p/xQYjRJrGxh/。
2.13
JOI 2023 Final T1 T2 T4
[ARC121E] Directed Tree(容斥 + 树形背包)
这个限制就是说:所有的 \(a_i\) 都不是 \(i\) 的祖先。
考虑 \(a_i\) 的逆排列 \(b_i\),那么所有的 \(b_i\) 都不在 \(i\) 的子树内。
这个就可以往容斥的方面想了:设 \(g_i\) 为钦定 \(i\) 个 \(b_j\) 在 \(j\) 的子树内的方案数,那么答案就是 \(\sum\limits_{i=0}^n(-1)^i g_i(n-i)!\)。
\(g_i\) 也很好求,可以使用树形背包:设 \(f_{u,i}\) 表示 \(u\) 子树内选了 \(i\) 个不合法的点的方案数。转移有:
- \(f_{u,i+j}\leftarrow f_{u,i}\times f_{v,j}\)。不同子树内的点不会相互影响。
- \(f_{u,i}\leftarrow f_{u,i-1}\times(sz_u-1-(i-1))\)。在 \(u\) 子树内选一个之前没有被选的点。
设 \(1\) 为根,那么 \(g_i=f_{1,i}\)。
代码:https://paste.ubuntu.com/p/QGPTmswWsk/。
[HNOI2015] 接水果(整体二分 + 扫描线 + 树状数组)
看到第 \(k\) 小和多组询问,马上想到整体二分。
对于路径覆盖,讨论一下是否是直链,然后就是两个端点在 DFS 序区间里就会有贡献。这个可以离线下来扫描线 + 树状数组求贡献。具体来说就是矩形加和单点查。
2.14
2023.2.14 考试 T1 序列(sequence)(K-D Tree + 历史最值)
题意:
给定一个长度为 \(n\) 的序列 \(A\),定义矩阵 \(B_{n\times n}\),初始时 \(B_{i,j}=\sum\limits_{k=i}^j A_k\)。
有 \(m\) 个操作,每个操作为一下两种之一:
- 给出 \(p,x\),将 \(A_p\) 修改为 \(x\),然后对所有 \(B_{i,j}\) 更新为 \(\min(B_{i,j},\sum\limits_{k=i}^j A_k)\)。
- 给出 \(l,r\),输出 \(B_{l,r}\)。
数据范围:\(1\le n,m\le 10^5,l\le r,0\le A_i,x\le 10^9\)。
序列上不太好做,考虑把每个区间 \([l,r]\) 看成一个点 \((l,r)\),那么操作就是矩形加、单点求历史最小值。
先把询问拿出来,用这些点建 K-D Tree,然后维护历史最值的 tag 就行了。
历史最值见 https://oi-wiki.org/ds/seg-beats/#历史最值问题。
代码:https://paste.ubuntu.com/p/PXdDC4mZf7/。
2023.2.14 考试 T3 旅行(travel)(枚举 + 最短路)
题意:
给定一张 \(n\) 个点 \(m\) 条边的带权无向图,对于一条路径 \(P\),其代价为所有边权中前 \(K\) 大的和(长度不足 \(K\) 就是所有边权和)。问 \(K=1\sim n\) 时从 \(1\) 到 \(n\) 的路径的最小代价是多少。
数据范围:\(n,m\le 3000\),保证 \(1\) 和 \(n\) 连通。
考虑枚举第 \(K\) 大的边权 \(d\)。把所有 \(\le d\) 的边权全部看成 \(0\)。设 \(f_{i,j}\) 表示 \(1\) 到 \(i\) 经过了 \(j\) 条边权 \(>d\) 的边最小的权值和,转移使用 Dijkstra。更新答案的时候直接把多余的边按照权值为 \(d\) 来算,因为画画图就知道如果 \(d\) 不是真实的第 \(K\) 大那么肯定是不优的。
考虑怎么优化。尝试用一条最短路更新所有的 \(K\)。还是枚举 \(d\),只是这一次忽略 \(f\) 数组的第二维,将所有边权 \(>d\) 的边先减去 \(d\) 再去转移,更新答案直接加上 \(K\times d\) 就行了。
代码:https://paste.ubuntu.com/p/JJKtN4krYd/。
[HDU4652] Dice(期望 + DP + 数学)
题意:
\(m\) 面的骰子,求:
- 出现 \(n\) 个连续的相同颜色的时候停止
- 出现 \(n\) 个连续的不同颜色的时候停止
的期望次数。
数据范围:\(1\le T\le 100,1\le n,m\le 10^6\)。
先考虑第一问,设 \(f_i\) 表示出现 \(i\) 个连续相同颜色的期望次数。
转移有:\(f_i=\frac{1}{m}f_{i+1}+\frac{m-1}{m}f_1+1\)。
同理有 \(f_{i-1}=\frac{1}{m}f_i+\frac{m-1}{m}f_1+1\)。
作差可得 \(f_i-f_{i-1}=\frac{1}{m}(f_{i+1}-f_i)\),即 \(f_{i+1}-f_i=m(f_i-f_{i-1})\)。
因为 \(f_1-f_0=1\),所以 \(f_n=1+m+m^2+\cdots+m^{n-1}=\frac{1-m^n}{1-m}\)。
再考虑第二问,设 \(g_i\) 表示出现 \(i\) 个连续不同颜色的期望次数。
有 \(g_i=\frac{m-i}{m}g_{i+1}+\frac{1}{m}\sum\limits_{j=1}^i g_j+1\)。
也有 \(g_{i+1}=\frac{m-(i+1)}{m}g_{i+2}+\frac{1}{m}\sum\limits_{j=1}^{i+1}g_j+1\)。
作差可得 \(g_{i+1}-g_i=\frac{m-(i+1)}{m}g_{i+2}-\frac{m-i}{m}g_{i+1}+\frac{1}{m}g_{i+1}=\frac{m-i-1}{m}(g_{i+2}-g_{i+1})\)。
递推计算即可。
代码:https://paste.ubuntu.com/p/nQ6CtyfJJk/。
[QOJ5510] The 1st Universal Cup. Stage 3: Poland / AMPPZ 2022 Problem L. Line Replacements(结论 + 贪心)
题意:
给定一棵树,边有边权,有一些点被标为关键点。
定义一个连通块是合法的当且仅当存在一组端点都是关键点的路径,使得每条边被覆盖的次数等于边权。
现在有一些边要被删除,确定一个删边的顺序使得每次删完一条边之后所有连通块都是合法的。或者判断无解。
数据范围:\(2\le n\le 5\times 10^5\)。
结论:连通块合法当且仅当,对于其中的每个点,要么它是关键点,要么和它相连的所有边的边权之和是偶数且没有绝对众数。
证明似乎不太会(雾)。应该可以考虑类似反证和贪心。
然后可以尝试正难则反,把删边变成加边。显然按照边权从小到大的顺序加是不劣的。于是这样贪心就可以通过本题。
代码:https://paste.ubuntu.com/p/bbqHxp9chd/。
2.15
2023.2.14 考试 T2 a(a)(DP + 分治)
题意:
给定 \(n,m\),求 \([0,2^n)\) 中,有多少个 \(x\) 满足 \(x\) 不为 \(m\) 的倍数并且 \(\exist 0\le k<n,m|(x\oplus 2^k)\)。
答案对 \(10^9+7\) 取模。
数据范围:\(T=5,n,m\le 2000\)。
首先容易得到 \(\mathcal{O}(m^2n)\) 的暴力 DP:枚举 \(x\bmod m\) 的值 \(y\),设 \(f_{i,j,0/1}\) 表示已经确定了前 \(i\) 位,此时 \(\bmod m\) 的值位 \(j\),之前是否存在一位可以作为 \(k\),转移显然。
考虑这个 DP 的实质是什么:找到第一个翻转之后可以使得 \(\bmod m\) 的值从 \(y\) 变成 \(0\) 的位。考虑把所有可能的位拿出来(设为第 \(p_i\) 位取 \(q_i=0/1\)),从前往后依次枚举第一个满足条件的位(即把之前的所有 \(p_j\) 设为 \(1-q_j\)),然后就是若干个区间做背包。
可以把贡献相同的放在一起处理,那么就是一个可撤销背包了。
讲得比较简略,具体见代码。
代码:https://paste.ubuntu.com/p/5TmVJG5Xx6/。
2023.2.15 考试 T1 游戏(game)(DP + 概率期望)
题意:
有 \(n\) 个随从,生命值分别是 \(a_1 ,\dots, a_n\)。罗曼诺夫发动了一次技能,会连续攻击 \(m\) 次,每次在当前生命值为正的随从中随机选择一个,使得它的生命值减一。当一个随从的生命值降到 \(0\) 时它就死掉了。
问期望能干掉几个随从,答案模 \(998244353\) 输出。
数据范围:\(n\le 15,m\le 200,a_i\le 200,m\le \sum a_i\)。
设 \(f_{S,i}\) 表示使用了 \(i\) 次攻击,集合 \(S\) 内的随从已经死掉了的概率,\(su_S\) 表示 \(\sum\limits_{i\in S}a_i\)。钦定第 \(i\) 次攻击一定会让一个随从死掉,那么转移就有 \(f_{S,i}=\sum\limits_{j\in S,k< i}f_{S/j,k}\times\binom{i-1-su_{S/j}}{a_j-1}\times (\frac{1}{n-|S|+1})^{i-k}\)。可以前缀和优化。
再设 \(g_{S,i}\) 表示使用了 \(i\) 次攻击,集合 \(S\) 内的随从都没有死掉的概率。转移是一个可重组合。
时间复杂度理论上是 \(\mathcal{O}(2^nm^2)\) 的,但是跑得飞快。
代码:https://paste.ubuntu.com/p/9DStqdPrGy/。
[洛谷 P4321] 随机漫游(期望 + 高斯消元 + DP)
设 \(f_{S,i}\) 表示已经走完了集合 \(S\) 内的点,现在在 \(i\) 号点,要走完所有点的期望次数。
假设所有 \(c_i\) 形成的点集是 \(T\),\(T\) 的补集是 \(T'\),那么答案就是 \(f_{T',s}\)。
转移有:\(f_{S,i}=1+\frac{1}{deg_i}\sum\limits_{(i,j)\in E}f_{S\cup\{j\},j}\)。
直接高斯消元是 \(\mathcal{O}((2^n\times n)^3)\) 的,显然无法通过。
我们考虑把 \(j\in S\) 和 \(j\not\in S\) 的情况分开来算。
即 \(f_{S,i}=1+\frac{1}{deg_i}\sum\limits_{(i,j)\in E,j\in S}f_{S,j}+\frac{1}{deg_i}\sum\limits_{(i,j)\in E,j\not\in S}f_{S\cup\{j\},j}\)。
可以把 \(f\) 数组按照 \(|S|\) 递减的顺序计算,这样就可以对于每一个 \(S\) 单独跑一遍高斯消元,时间复杂度降为 \(\mathcal{O}(2^n\times n^3)\),可以通过。
代码:https://paste.ubuntu.com/p/jMXfPfWKP2/。
2023.2.15 考试 T2 排序(sorting)(构造 + 康托展开)
题意:
给出一个排序算法:
从排列的开头开始,判断每一对相邻的数是否大小关系正确。
如果存在相邻的一对数大小关系不正确:
- 把较小的那个数丢到排列开头。
- 回到步骤一。
排列有序了,结束。
对于排列 \(p\),设 \(F(p)\) 表示把 \(p\) 排序会需要进行多少次步骤二。
类似字符串,可以定义两个排列(允许长度不同)的字典序比较。
给定 \(K\),求字典序最小的排列 \(p\),使得 \(F (p) = K\)。可以证明解必定存在。
数据范围:\(1\le K\le 10^{18}\)。
给出结论:对于一个排列 \(p\),设 \(d_i=\sum\limits_{j<i}[p_j<p_i]\),那么 \(F(p)=\sum 2^{d_i[d_i\ne i-1]}\)。
那么构造就比较显然了,具体见代码吧。可能不太好讲清楚。
代码:https://paste.ubuntu.com/p/7HqDYwsRnm/。
2.16
[ARC096F] Sweet Alchemy(多重背包 + 贪心)
挺神奇的一道题吧。
可以把每个子树看成 \(d\) 个物品,代价为子树内 \(\sum m_i\),价值为子树大小,跑多重背包即可。
然而 \(d\) 很大,显然过不去。
考虑一个显然错误的贪心:设 \(v_i\) 为价值,\(w_i\) 为代价,那么将所有物品按照 \(\frac{v_i}{w_i}\) 从大到小排序后贪心地能放就放。这是因为假设 \(\frac{v_i}{w_i}>\frac{v_j}{w_j}\),选择 \(v_j\) 个 \(i\) 物品和 \(v_i\) 个 \(j\) 物品价值相等,但是前者代价更少,显然更优。
因此选择了 \(i\) 之后最多选择 \(v_i-1\) 个 \(\frac{v}{w}\) 比 \(i\) 小的物品。
所以只需要每种物品拿出来 \(\min(n,d)\) 个跑多重背包二进制分组,剩下的贪心就行。
代码:https://paste.ubuntu.com/p/5qd3qkSDdT/。
[ARC118D] Hamiltonian Cycle(构造 + 数学)
我们可以找到最小的正整数 \(n\) 满足 \(a^n\equiv 1\pmod P\)。设集合 \(H=\{a^i|0\le i<n\}=\{a^i|i\in\mathbb{Z}\}\)。
同样可以找到最小的正整数 \(m\) 满足 \(b^m\in H\)。
显然每个数在 \(\bmod P\) 意义下都可以被表示成 \(a^ib^j(0\le i<n,0\le j<m)\) 的形式。
考虑一个结论:所有 \((i,j)\) 都是互不相同的。证明显然,可以反证。
所以有解的充要条件就是 \(nm=P-1\)。
接下来考虑构造。建一张 \(n\times m\) 的表格,\((i,j)\) 上写的是 \(a^ib^j\bmod P\),序列可以映射成一条经过起点以外每个格子一次并且最后回到 \((0,0)\) 的回路。构造方法见题解的图。
https://atcoder.jp/contests/arc118/editorial/1210。
代码(第一种构造):https://paste.ubuntu.com/p/5SHM8PnqZR/。
[ARC119E] Pancakes(贪心)
假设选择的区间是 \([l,r]\)。
先把选择的区间有一端是边界的情况判掉。
然后发现贡献的变化量是 \(|a_r-a_{l-1}|+|a_{r+1}-a_l|-(|a_l-a_{l-1}|+|a_{r+1}-a_r|)\)。
手玩一下可以发现只有当 \([a_{l-1}<a_l]=[a_r<a_{r+1}]\) 的时候答案才会减少。
具体可以见官方题解的这一张图(假设 \(a_{l-1}<a_l\),反过来的情况是对称的):

我们惊奇地发现:如果把 \([a_{i-1},a_i]\) 看成一条线段,那么答案减少的量就是两条线段的交的长度的两倍!
所以可以把相邻数大小关系相同的线段拿出来,按照左端点排序,记录一个前缀的线段中最大的右端点,这样可以求出最长的线段交。
代码:https://paste.ubuntu.com/p/RqVMQnKZGh/。
[CF1513F] Swapping Problem(贪心)
和上一题一样的套路?只不过是方向不同的线段才会产生贡献。
把所有线段都拿出来一起按照左端点排序,对两种方向的线段分别存下最大的右端点,可以方便地求出最长的线段交。
代码:https://paste.ubuntu.com/p/MBXM2DJKHr/。
2.17
Educational Codeforces Round 143 (Rated for Div. 2)(CF1795)A~G
[CF1795E] Explotions?(单调栈 + DP)
注意到炸弹只能用一次,因此考虑计算在每个位置使用炸弹最多能节省多少能量。
显然一个位置左边和右边的情况对称,因此下面只考虑左边的情况。
如果一个位置节省了 \(a_i\),那么离它距离为 \(x\) 的就最多只能节省 \(a_i-x\)(而且不能高于它原始的高度)。固定前缀的话这大概是一个若干斜率为 \(1\) 的一次函数取 \(\min\) 的形式。画个图可以更好地理解。可以通过单调栈 + DP求出这个量。
具体可以见代码。
代码:https://paste.ubuntu.com/p/cXWwmt5xkQ/。
[CF1795F] Blocking Chips(二分 + 贪心 + DP)
先二分 \(mid\),判断在 \(mid\) 时刻可不可以完成这个过程。
显然每个芯片移动的路径都是一条以它的初始位置为起始点的链。
按照深度从大到小贪心。
记录 \(giv_i\) 表示点 \(i\) 向下最多还能延伸多少,\(ned_i\) 表示 \(i\) 还需要向下延伸多长,\(del_i\) 记录 \(i\) 是否已经被覆盖。
如果一个点不需要延伸并且未被覆盖,那么它就可以由父亲向下延伸。转移 \(giv_{fa_i}\leftarrow\max(giv_{fa_i},giv_i+1)\)。
否则如果一个点最多能向下延伸的长度还达不到它需要的长度,它就需要向上跳父亲并且在某个祖先那里再延伸下去。如果它的父亲已经被覆盖就无解。
最后如果 \(1\) 号点还需要延伸就说明无解。
代码:https://paste.ubuntu.com/p/QFQzKzcSv4/。
[CF1795G] Removal Sequences(分块 bitset + 可达性统计 + 拓扑排序)
注意到题目说至少有一组解。这个限制实际上是很强的。
我们可以求出 \(a'_i\) 表示 \(i\) 需要删掉多少条邻边之后才能被删掉。如果 \(a'_i=0\) 就说明这个点一开始就需要被删。
先把所有 \(a'_i=0\) 的点删掉,然后继续删边更新 \(a'\),类似拓扑排序的过程可以得到一个删点顺序。显然因为有解,所以这是一张 DAG。
考虑这张 DAG 实际上就是钦定某两个点之间被删的先后顺序。跑一遍可达性统计,那么一个点的后继就都不能在它前面被删。卡空间可以使用分块 bitset 的方法,具体来说就是每次只考虑一段区间内的编号。
代码:https://paste.ubuntu.com/p/Nyf2xn62c6/。
[洛谷 P3600] 随机数生成器(期望 + DP)
可以考虑使用经典套路:\(E(x)=\sum\limits_{i=0}^{\infty}i\times(P(x\le i)-P(x<i))\)。
一个显然的性质就是:一个区间包含另一个区间时,大的那一个区间没有用。因此可以直接删去。
那么我们可以钦定所有询问结果的最大值为 \(y\),将所有 \(\le y\) 的数看成 \(0\),\(>y\) 的数看成 \(1\),限制条件就变成:每个区间内至少要有一个 \(0\)。
考虑 DP 方案数:设 \(f_{i,j}\) 表示考虑了前 \(i\) 个位置,钦定 \(i\) 填的是 \(0\),已经填了 \(j\) 个 \(0\) 且满足所有限制条件的方案数。\(f_{i,j}\) 从 \(f_{k,j-1}\) 转移过来,当且仅当 \((k,i)\) 没有包含任何一个完整区间。可以通过计算 \(mx_i\) 表示右端点 \(<i\) 的区间中左端点的最大值来方便地求出 \(k\) 的区间,即 \([mx_i,i)\),然后就可以前缀和优化了。
接下来考虑求概率 \(P(x\le i)\)。先求出 \(g_i\) 表示填了 \(i\) 个 \(0\) 且满足条件的方案数,那么 \(P(x\le i)=\frac{\sum\limits_{j=1}^n g_j\times i^j\times(x-i)^{n-j}}{x^n}\)。
然后就做完了。
代码:https://paste.ubuntu.com/p/whKn6k8x5S/。
[CF1483F] Exam(AC 自动机 + 树状数组)
对于每一个 \(i\),满足条件的 \(j\) 最多只有 \(|s_i|\) 个。因为所有 \(j\) 匹配的终止位置一定不一样。
枚举较长串,枚举短串的右端点,找到最大匹配长度(即找到一个最远的左端点满足两端点之间的子串是 \(s\) 集合中的),显然只有这个最长的字符串会对答案有贡献。
但是这样会算重,如何解决?
发现只有 算到的次数 = 在长串中出现的次数 的情况对答案有 \(1\) 的贡献。
算到的次数可以把所有钦定的右端点对应的最长串拿出来,从后往前扫,只统计不被包含的字符串。在长串中出现的次数可以用 树状数组 + ACAM 来维护。
代码:https://paste.ubuntu.com/p/zkcMVVqCDh/。
2.18
[CTSC2018] 假面(概率期望 + 可撤销背包)
第二问很好做。直接维护 \(f_{i,j}\) 表示第 \(i\) 个单位剩余血量为 \(j\) 的概率。转移直接从前往后扫,分技能命中 / 不命中两种情况讨论。注意血量为 \(0\) 时不需要从不命中转移而来。
对于第一问,实际上就是求每个人 \(i\) 存活时,期望有多少个人存活。可以考虑 DP:设 \(g_{i,j}\) 表示前 \(i\) 个人有 \(j\) 个存活的概率。转移显然。容易发现转移顺序和人的位置无关。
那么我们直接把第 \(i\) 个人放到最后,设 \(P_i\) 为 \(i\) 存活的概率,\(Q_i\) 为 \(i\) 死亡的概率。可以得到 \(g_{k,j}=g_{k-1,j}\times Q_i+g_{k-1,j-1}\times P_i\)。再设 \(f_{u,i}\) 表示不考虑 \(u\) 有 \(i\) 个人存活的概率。显然有 \(f_{u,0}\times Q_u=g_{k,0}\),\(f_{u,i-1}\times P_u+f_{u,i}\times Q_u=g_{k,i}\)。递推即可计算出所有的 \(f_{u,i}\)。注意当 \(Q_u=0\) 时有 \(f_{u,i}=g_{k,i-1}\),需要特判。
代码:https://paste.ubuntu.com/p/kPbTs9H9qk/。
The 1st Universal Cup. Stage 4: Ukraine 部分题目
2.19
[USACO 2022 US Open Platinum] Hoof and Brain(启发式合并 + 拓扑排序 + 性质)
如果有一个棋子所在的节点出度为 \(0\),那么甲只要选择这一个棋子就能获胜。
那么我们就可以标记这些节点并将它们删去,这时候可能又会有新的节点出度为 \(0\),将它们继续删去后迭代这一过程。剩下的节点都是出度 \(>0\) 的了。
现在我们考虑一个只有 \(1\) 条出边的节点 \(x\),假设这条边指向的节点是 \(y\),那么对于任意 \(x\) 上的棋子,移动后都一定会到达 \(y\)。如果甲能够通过让两个棋子都移动到 \(x\) 获胜,那么甲显然也能够通过让两个棋子都移动到 \(y\) 获胜。于是我们可以将 \(x\) 和 \(y\) 合并,具体来说,我们对于每条边 \(z\to x\),将其删除,然后若 \(z\to y\) 不存在则加入边 \(z\to y\)。和之前相同,此时可能又会有新的节点出边数量变为 \(1\),我们需要将它们继续合并。
合并后,新图中的所有点都有至少 \(2\) 条出边,或有一个自环。先考虑所有点都至少有 \(2\) 条出边的情况,显然乙的每次操作都能够避免使两个棋子进入同一个节点。而有自环的情况同样平凡。
于是,对于询问 \((x,y)\),我们可以按照如下方式判定胜负:
若 \(x\) 或 \(y\) 被删除,那么甲必胜。否则,如果 \(x\) 和 \(y\) 被合并成了一个节点,那么甲必胜,否则乙必胜。
使用启发式合并 set 维护每个节点的出边集合 \(out\) 和入边集合 \(in\),并查集维护合并后每个点所在的集合,时间复杂度 \(\mathcal{O}(m\log n+q)\)。
2.20
[JOI Open 2022] 放学路(性质 + 广义串并联图方法)
关于“广义串并联图方法”,可见 https://www.cnblogs.com/lpf-666/p/16602167.html。
实质上就是通过一系列点的合并操作来使得图的性质得到保留,并且答案可以方便地得到。
新建一条边 \((1,n,L)\),那么和 \(1,n\) 不在同一点双内的所有点都不可能被经过。
接下来只考虑和 \(1,n\) 在同一点双内的点。
- 度数 \(\le1\) 的点(除 \(1,n\) 外),显然这个点和它所连的边不会被经过,可以直接删去。实际上由于这个点和 \(1,n\) 在同一点双内,这种情况永远不会出现。
- 度数 \(=2\) 的点(除 \(1,n\) 外),将它相连的两条边合并成一条边,边权相加,标记取或。
- 如果出现了重边,边权相同可以忽略,不同则说明有不同于最短路的路径,将其标记。
最终如果 \(2\sim n-1\) 中有未被删除的点,或者 \(1\to n\) 的边被标记了,则说明有解。否则无解。
用 set 维护与每个点相连的边即可。
证明可见 https://www.cnblogs.com/PYWBKTDA/p/17092052.html。
[UOJ792]【UR #24】比特跳舞(DP + 性质)
首先有一个暴力 DP:设 \(f_{i,j}\) 表示到位置 \(i\),以元素 \(j\) 结尾的本质不同序列的方案数。
转移有:
- 若 \(j=a_i\),则 \(f_{i,j}=1+\sum\limits_{1\le k<n}f_{i-1,k}\);
- 若 \(j\ne a_i\),则 \(f_{i,j}=f_{i-1,j}\)。
在序列后加一个值的时候 \(f\) 只会有一个位置改变。
注意到我们只关心最终本质不同序列个数的奇偶性。
设 \(f_{i,0}=1+\sum\limits_{1\le k<n}f_{i,k}\)。那么新加入一个数 \(a_i\) 后:
- \(f_{i,a_i}=1+\sum\limits_{1\le k<n}f_{i-1,k}=f_{i-1,0}\);
- \(f_{i,0}=1+\sum\limits_{1\le j<n,j\ne a_i}f_{i-1,j}+f_{i-1,0}=2f_{i-1,0}-f_{i-1,j}\)。
注意到 \(2f_{i-1,0}-f_{i-1,j}\) 和 \(f_{i-1,j}\) 的奇偶性是相同的。
也就是说,新增一个值 \(a_i\) 就相当于交换了 dp 数组中下标为 \(0\) 的位置和下标为 \(a_i\) 的位置的值!
初始时,\(f_{0,j}=[j=0]\)。于是任意时刻,dp 数组中正好有一个奇数,一个序列的本质不同子序列个数为奇数,当且仅当最终这个奇数在下标 \(0\)。
由该结论可以得出,一个序列的本质不同子序列个数为奇数,当且仅当它可以划分为若干个长度至少是 \(2\) 的子串,使得每个子串的开头和结尾的数相同,且这个数没有在子串中其它位置出现过。我们称每个这样的子串为极短合法序列。
然后发现合法关系实际上是一个等价关系,合并等价类即可。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号