2024.10
To-Do List:ABC373,374G,MX-X6 4,CF2021D.
[ABC267G] Increasing K Times
将对排列计数转化为重排 \(a\) 求方案数。钦定要从小到大插入数。自然想到设 \(f_{i,j}\) 表示插入了前 \(i\) 个数,增出现了 \(j\) 次的方案数。
现在我要插入 \(a_i\),它之前出现的次数为 \(cnt\)。
如果 \(i\) 放第一个数,不会增加。如果 \(i\) 放在一个增中间,会增加一个减少一个。如果 \(i\) 放在相同数后面不会增加。否则都会增加。
注意这里的插入对于一段没有插入数的区间是看做一个整体的,因为后面会自然地让其挪动,所以空位只有 \(i+1\) 个。
那么 \(f_{i,j}=f_{i-1,j-1} \times (i-j-cnt)+f_{i-1,j} \times (j+cnt+1)\)。
重排或排列,我们可以钦定顺序后一个一个插入来解决问题。
CF2005B The Strict Teacher
怎么会是呢。
如果教师都在这个人的一边,那显然要走到头。
如果教师在两边,那应该走到左右两名教师位置的中位数上,这个是总能走到的。这样相当于两个都最难抓住他。
CF2005C Lazy Narek
考虑 dp。设 \(f_{i,j}\) 表示考虑了前 \(i\) 个串,目前 A 匹配到了 narek 的前 \(j\) 个字符的最大差值,转移如果选这个串就暴力计算,不选就直接把 dp 值复制过去。最后答案为 \(\max(\max_{0\le i\le 4}f_{n,i}-2i,f_{n,5})\),因为没匹配完的那部分不能得到分数,这部分分数要给 B。
CF2005E1 Subtangle Game (Easy Version)
没看懂官方题解,但是不是暴力就行啊?
设 \(f_{k,i,j}\) 表示第 \(k\) 个数放在 \((i,j)\) 是否必胜。那么 \(b_{i,j}=a_k\),然后如果 \((i+1,j+1)\) 下面有必胜点这个点就必败,记一下上一轮的二维前缀和即可。初值是 \(f_{n+1,?,?}=0\)。先手必胜当且仅当 \(f_{1,?,?}=1\)
P2679 [NOIP2015 提高组] 子串
如果只设 \(f_{i,j,k}\) 表示考虑了 \(A\) 的前 \(i\) 个,匹配了 \(B\) 的 \(j\) 个,划分了 \(k\) 段,转移比较困难,因为你不知道上一个选的是在哪里,转移需要枚举断点,无法通过。
考虑再加一维,设 \(f_{i,j,k,0/1}\),最后一维表示当前位选了没有,那么如果这一位不选,显然 \(f_{i,j,k,0}=f_{i-1,j,k,0}+f_{i-1,j,k,1}\)。如果这一位要选,那么它既可以是新的一段,也可以连着之前的一段。新的一段方案数是 \(f_{i-1,j-1,k-1,0}+f_{i-1,j-1,k-1,1}\)。连着的方案数是 \(f_{i-1,j-1,k,1}\)。注意这些转移有条件,比如第一种转移要满足 \(j,k\neq 0,a_i = b_j\)。时间复杂度 \(\mathcal{O}(nmk)\)。
[ABC184D] increment of coins*
假设当前有 \(i\) 个 A,\(j\) 个 B,\(k\) 个 C,那么选出来某种数的概率是容易求的。问题是应该用 \(i+1\) 更新 \(i\) 还是用 \(i\) 更新 \(i+1\) 呢?
我们要求的是期望,也就是 \(f_{i,j,k}\) 表示 \(i\) 个 A,\(j\) 个 B,\(k\) 个 C 到合法的期望步数。而终止状态的期望是已知的,我们应该通过已知推未知,所以应该是做一个倒推。\(i\) 和 \(i+1\) 转移的概率确定,就是 \(\frac{i}{i+j+k}\),实际过程是由 \(i\) 到了 \(i+1\),那么应该是从 \(i+1\) 转移过来。
其实正序递推也是可以的,但是有多个终态,还需考虑它们的概率。
[ABC252G] Pre-Order*
先序遍历就是按照 dfs 序遍历,一个子树的编号是一段区间。设 \(f_{l,r}\) 表示当前的根是 \(l\),子树在区间 \([l,r]\) 的方案数。显然其第一个子树为 \(l+1\)。
如果 \(l\) 只有一个子树,那么方案数是 \(f_{l+1,r}\)。如果 \(l\) 有多个子树,枚举它第一个子树走到了 \(l \le k<r\),只要我们的第一个子树不同,枚举出的方案就是不重不漏的。在 \(k\) 后面有若干个子树,第一个子树的根的编号为 \(k+1\),这部分的方案恰好为 \(f_{k,r}\),为什么不是 \(f_{k+1,r}\),因为后面的若干个子树应该是同级的,但是用 \(f_{k+1,r}\) 会分出三层,至于 \(f_{k,r}\),即 \(k\) 有一堆子树,第一个是 \(k+1\),根据定义即可知道这样做的正确性,所以这里的方案数就是 $f_{l+1,k} \times f_{k,r} $。因为 \(l+1,k+1\) 是同级子树,所以它们的编号应该递增,即 \(a_{l+1}<a_{k+1}\)。
P11145 「SFMOI Round I」Strange Homura Game
好玩。假设我们询问了 \(x\),交互库返回 \(r\),即 \(x \equiv r (\bmod m)\),那么容易得到 \(x-r \equiv 0(\bmod m)\),所以 \(x-r-1 \equiv -1 \equiv m-1(\bmod m)\),然后就做完了。
[ABC373D] Hidden Weights
因为 \(x_v=x_u+w\),所以 \(x_u=x_v+(-w)\)。为什么要加这句话呢?因为我们找的时候不能保证每次走的都是我们加边的方向,而这两两个条件互为充要,只需满足一个即可。我们可以寻找连通块,每次从某个点开始 bfs,同时设它的权值为 \(0\),然后顺着更新即可。
[ABC373F] Knapsack with Diminishing Values*
别乱赋初值啊!
首先 dp 式子是 \(f_{i,j}\) 表示前 \(i\) 个数占用了 \(j\) 的空间的答案。\(f_{i,j}=\min_{0 \le k \le \lfloor \frac{j}{w_i}\rfloor} f_{i-1,j-kw_i}+kv_i-k^2\)。有好多种优化方式。
方法一:考虑如果 \(w_i\) 互不相同总复杂度就是调和级数,所以把 \(w_i\) 一样的拿到一块处理,记 \(Q_{i,j}\) 表示价值为 \(i\) 的东西拿了 \(j\) 个的最大收益。考虑一个物品,买一次有 \(v_i-1\) 的收益,买两次则是 \(2v_i-4\),三次就是 \(3v_i-9\),相当于每次分别增加了 \(v_i-1,v_i-3,v_i-5\) 这样,因此对于每种 \(w_i\),可以开一个优先队列,每次拿出增量贡献最大的那个,然后将这个增量减 \(2\) 再塞回去。
方法二:按照 \(\bmod w_i\) 分组后直接决策单调性。(待补)
方法三:按照 \(\bmod w_i\) 分组后直接斜率优化。(待补)
[ABC373G] No Cross Matching
[ABC374E] Sensor Optimization Dilemma 2
二分。问题转化为,有一个 \((a_i,p_i)\) 和 \((b_i,q_i)\),求一对最小的 \(x,y\) 使得 \(a_ix+b_iy \ge mid\) 且 \(p_ix+q_iy\) 最小。
这个问题实际上是一个背包。那背包就有一个不完全正确的贪心做法:优先选性价比高的,这个东西其实在大部分时间内是正确的。就是最后可能直接超出去了导致不优。
那么多大范围内是正确的呢?本题的特点是 \(a_i,b_i\) 很小且只有这两种机器可选,那么这两种机器都能恰好表示出的价格一定是选性价比高的好。也就是若干个 \(\operatorname{lcm(a_i,b_i)}\) 都可以直接使用贪心。
那么将 \(mid\) 对 \(\operatorname{lcm(a_i,b_i)}\) 取模后就可以直接算了。但是还可以更简单。考虑前面选的都是一种机器,最后这些如果选了另一种机器也不会超过 \(\operatorname{lcm(a_i,b_i)}/a_i\) 个,也就是 \(\frac{b_i}{\gcd}\) 个,直接枚举计算即可。时间复杂度是 \(\mathcal{O}(na_i\log 10^9)\)。
[ABC374F] Shipping*
肯定是每次发货连续的一段最优,证明?你把中间空着的那些位置也插上人岂不是更好。然后如果没有 \(X\) 的限制发货时间肯定是这段人 \(T\) 的最大值。但现在有 \(X\) 的限制,那么如果在 \(T_{\max}\) 时间我们发不了货会等到上次发货 \(+kX\) 的时间发货,上次也是如此,最初,应该是某个 \(T_{\max}\) 时间发货,所以发货时间可以表示为 \(T_j+kX\)。这里 \(0 \le j \le N,0 \le k\le N\)。\(j\) 可以取 \(0\) 是因为我可以已知都是在 \(kX\) 的时间发货,这样我们应该令 \(T_0=-x\),因为从 \(0\) 出发的任何转移都是走到的 \(T_?\),所以这个时间应该小于所有的 \(T\);\(k\) 是因为有一次 \(+kX\) 不到 \(T_{\max}\) 就清零了,所以每段转移之间最多加上一个 \(X\)。
这样就可以把时间塞到 dp 状态里了,显然我们只需要最小化 \(\sum_{S_i}\),那么设 \(f_{i,j,k}\) 表示前 \(i\) 个物品,这一批发货时间为 \(T_{j}+k\) 的最小代价。
考虑转移,如果之前的时间 \(+X\) 比 \(T_i\) 大的话,那么 \(j\) 不变,否则 \(j=i,k=0\)。所以直接填表会更加轻松,因为 \(j,k\) 是在变化的。
[ABC374G] Only One Product Name
咕咕咕。
P11158【MX-X6-T4】夢重力
P11159【MX-X6-T5】再生
容易得到每条长链的长度。对于这条链来说,它的顶必须是给定的,其他点顺序不重要,所以方案数为 \((siz-1)!\)。现在已经有了这些链的方案数,只需要把它们拼起来。对于两条长链,长度分别为 \(x,y,(x<y)\),把 \(x\) 拼到 \(y\) 上不能改变 \(y\) 的长度,那么一共有 \(y-x\) 种方案。现在有若干条链,把他们按照长度从大到小排序后每条链依次拼接,链 \(i\) 的方案数就是 \(\sum_{j<i} siz_j-siz_i\),记个前缀和即可做到线性。
这样的条件一定是完整的吗?从剖分的角度看,整棵树是若干条链,我们计算了链内部的方案数和链之间组合的方案数,显然这已经足够了。所以对于计数问题,我们就是要考虑每个小部分和这些部分之间的组合。
P11160【MX-X6-T6】機械生命体
CF1971H ±1
如果中间一行是都是 \(1\),那么每列最多有一个 \(-1\)。换句话说,如果一个数选了 \(-1\),那么这一列其它的数必须选 \(1\)。
这是很明显的 2-SAT 约束条件,建图后跑一遍 tarjan 即可。
CF487B Strip
设 \(f_i\) 表示 \([1,i]\) 最少划分成几段。
那么 \(f_i=\min_{j \le i-l,\max_{p\in(j,i]}a_p-\min_{p \in (j,i)}a_p \le s}f_j+1\)。
最随着 \(i\) 增大,最大值不会减小,最小值不会增大,所以它是单调的,双指针即可。RMQ 只会用 st 表。这样单调队列优化就做完了。
CF1731E Graph Cost
先考虑加边,那你肯定要知道边权为 \(i\) 的边有多少条。这其实就是有多少个数对 \(1 \le i < j \le n\),满足 \(\gcd(i,j)=k\),记这个问题的答案为 \(f_k\)。
首先 \(i,j\) 都要是 \(k\) 的倍数,根据小学数学,方案数是 \(\frac{\lfloor \frac{n}{k} \rfloor (\lfloor \frac{n}{k}-1 \rfloor)}{2}\)。但是还要减去 \(\gcd\) 是 \(k\) 的倍数的情况,那就是直接枚举倍数就行了,复杂度调和级数。
然后加 \(k\) 条边的代价是 \(k+1\),所以答案是 \(m\) 加上加边次数,那我们想让加边次数尽量小,所以要优先用大的 \(k\)。如果我们想要加 \(k\) 条边,那么就需要有 \(k\) 条权值为 \(k+1\) 的边,也就是 \(f_{k+1 }\ge k\),一直操作知道这个条件不满足,最终的答案就是正确的。因为前面用大的也不会让后面的小的用不了。但是直接暴力模拟操作也不行,要通过一些计算得到加 \(i\) 要操作几次。应该是 \(\min(\lceil \frac{m}{k} \rceil,\lfloor \frac{f_{k+1}}{k} \rfloor)\),\(m\) 是当前的余量。
[ABC159F] Knapsack for All Segments
背包是在线求的。所以跑完前 \(i\) 个数后的背包存的就是 \(r\le i\) 的方案数。
做法一:考虑直接对这个问题 dp。设 \(f_i\) 表示和为 \(i\) 的答案,那么每次 \(i\) 移动时只需要给 \(f_0\) 加一,然后做一遍背包即可,因为可供选择的左端点又多了一个。
做法二:考虑对每种方案算贡献。对于一个子序列,假设它选取的最左元素为 \(l\),最右元素为 \(r\),那么贡献就是 \(l(n-r+1)\)。在整个背包的过程中加数,如果这个数是某个子序列的开头,也就是它是由 \(f_0\) 转移而来,那么将 \(f_{a_i}\) 加上 \(i\);如果当前这个数是某个子序列的结尾,那么直接使用 \(f_{S-a_i}\) 就是前面所有方案选择的 \(l\) 之和,乘上 \((n-i+1)\) 即可。
有一点细节:为防止自己更新自己,所有贡献都要在背包前计算,但这样不能计算他自己作为一个序列的方案,但除非 \(a_i=S\),否则序列中肯定不止有它这一个数,所以还是好算的。
[ABC169F] Knapsack for All Subsets
类似上一题考虑对某个方案算贡献。对于一个选取的子序列,它有贡献的子集必然选了这个子序列中的所有数,对于其他数则可以随便选。所以假设这个子序列选了 \(x\) 个数,它对答案的贡献就是 \(2^{n-x}\)。
那么设 \(f_0=2^n\),然后每次转移意味着多选了一个数,可以随便选的地方又少了一个,方案数除二(即乘上 \(2\) 的逆元)即可,最后输出 \(f_S\) 就是答案。
CF1634F Fibonacci Additions*
首先判等转化为维护 \(a_i-b_i\) 看是否全 \(0\),然后可以用 CF446C 的做法,不保证能过。
考虑一个简单的小问题,区间加,整个序列是否全都为 \(0\),怎么做。当然可以线段树暴力,也可以用差分维护,差分数组全 \(0\) 和原数组全 \(0\) 是等价的。
这题我们也考虑差分,但是原始的差分定义是绝对不行了,因为 \(f_{i}=f_{i-1}+f_{i-2}\),想让中间的值不变的话,就需要消掉 \(f_{i-1}\) 和 \(f_{i-2}\),而这两个数恰好贡献在了 \(i-1\) 和 \(i-2\) 两个位置上。
因此:记 \(c_i=a_i-a_{i-1}-a_{i-2},c_1=a_1\),我们只需要实时维护 \(c\) 中 \(0\) 的数量即可。每次修改只有 \(l,r+1,r+2\) 三个地方的值会变化。其实应该有四个,但因为 \(f_{1}=f_{2}\) 所以可以忽略。
那么 CF446C 能不能也这么做呢?咕。
CF1702F Equate Multisets
先乘二再除二没有意义,所以钦定先做能做的所有的乘二操作,那么 \(a,b\) 中的 \(2\) 全都没有影响可以去掉,接下来的问题就是两个奇数集合 \(a,b\),只能对 \(b\) 中的元素除二下取整,能否相等。
每次取出最大元素:\(ma,mb\)。
若 \(ma>mb\),没有让 \(b\) 扩大的操作,无解。
若 \(ma<mb\),把 \(mb\) 除二即可。
若 \(ma=mb\),直接匹配,要是再操作就无解了。
CF1702G Passable Paths
做法很多。
算法一:找到一条最长的链,然后判断给定的点是否都在链上。找最长链可以先找一个最深的点,再找离它最远的点;判断可以用树剖或直接用 \(dis(u,x)+dis(x,v)=dis(u,v)\)。
算法二:建出这些点的虚树,检查虚树是否是一条链。也可以在虚树上分讨,但是比较麻烦。
但是呢,只有我建了虚树后求直径又树剖,喜提 3.8K 屎山。这个做法的正确性基于合法情况的虚树一定是一条链,直径唯一。
[AGC058B] Adjacent Chmax*
CF1906J Count BFS Graph*
[ABC264F] Monochromatic Path
每个翻转最多用一次,设 \(f_{i,j,0/1,0/1}\) 表示在 \((i,j)\),行是否翻转,列是否翻转,即可。
CF2021C Adjust The Presentation
条件等价于 \(a\) 的每个数在 \(b\) 中出现的位置单调递增,没出现当然可以,所以设为 \(m+1\)。原因是 \(a\) 中的人要依次出场,出场后就可以随便插,但不递增就意味有的人前面的人还没走他就想走,显然他走不了。
用 set 维护每个数的所有出现位置,每次修改的时候修改每个数的第一次出现位置和不递增对的数量即可。
CF2021D Boss, Thirsty
CF2021E2 Digital Village
算法一:Floyd。不知道
算法二:建出 Kruskal 重构树后 dp,设 \(f_{i,j}\) 表示 \(i\) 的子树中设了 \(j\) 个关键点。假设它的子树分别为 \(ls,rs\),关键点的数量为 \(S_x\),点权为 \(w_x\)。
那么首先左右两个子树可以合并,即 \(f_{x,i+j}=\min(f_{ls,i}+f_{rs,j})\)。然后在考虑左右两边有一边没放的情况,那么这边的点必须跨过 \(x\) 来和另一边的点联通,就是 \(f_{x,i}=\min(f_{y,i}+S_{y'}w_x)\)。
这个 dp 只在重构树新建的节点上运行。对于原树上的点,只更新一下 \(siz\) 和 \(f_{x,1}=0\) 即可。这代表了在这里建点的决策。而代价都是在上面算的。
这其实也是基于一个贪心的想法,对于 \(ls,rs\),它们内部的关键点一定不会舍近求远走到 \(x\),除非没有点在它的内部。
时间复杂度 \(\mathcal{O}(n^2)\),空间要开 \(2n^2\) 个 long long,只能通过 E1。将 dp 数组换成 vector,更新完以后将其清空即可通过 E2。
[ARC185A] mod M Game 2
请注意 \(n<m\),因此每次会让一个人输的牌是唯一确定的,只要我不选它就行了,所以一定能进行到最后一轮。
考虑最后一轮的样子,首先 Alice 出牌,因为这已经是最后一轮,所以假设 Bob 还剩一个 \(x\),那么 Alice 出完牌后排堆里的牌的和就容易确定为 \(n(n+1)-x\),如果这个数是 \(m\) 的倍数,Alice 就输了。
接下来分析如果有一个 \(x\) 满足 \(n(n+1)-x \equiv 0(\bmod m)\),Bob 能不能把它留到最后。如果 Bob 有至少三张牌,而不能用的牌只有一张,所以 Bob 一定能留下这张牌。如果 Bob 有两张牌,且现在他不打出这张牌,那么他出完牌后排堆里的和是 \(n(n+1)-x-a\),其中 \(a\) 是 Alice 的最后一张牌。前面的一部分是 \(x\) 的倍数,而 \(-a\) 因为绝对值小于 \(m\),因此必然不是 \(0\),所以 Bob 不会输。
因此,如果 \([n(n+1)-n,n(n+1)-1]\) 中有 \(m\) 的倍数就是 Bob 赢,否则 Alice 赢。
这道题的想法大概就是从最后一轮的简单状态入手得到胜负的条件(猜测结论),然后再去解决原问题,这个思路与 mod M Game 也挺像的。
[ARC185B] +1 and -1*
前面加后面减,感性理解一下,这样最后如果能得到一个不降序列它应该是比较均匀的。又因为操作不改变和,所以大概就是就是一个 \(B=(x,x,\dots,x+1,x+1)\) 这样,\(x=\lfloor \frac{S}{n}\rfloor\)。
然后你要看看 \(A\) 能不能变成 \(B\),你从 \(1 \sim n\) 一次检查,维护只进行了前半部分的操作的数量 \(now\),如果当前 \(A_i>B_i\),那么 \(A_i\) 要变小,那应该作为右端点被操作,也就是 \(now\) 应当减去 \(|A_i-B_i|\),否则,需要一些操作补齐,\(now\) 应当加上 \(|A_i-B_i|\),显然 \(now\) 应当一直非负,否则就无解。
这为啥是对的呢?其实就是考虑如果它变成了一个不降序列,对于前面小的我们加,后面大的我们减,就一定能把它转成 \(B\)。
这道题的想法就大概是贪心,把问题规约到限制最紧的情况解决。
[ARC185D] Random Walk on Tree
这个树的形态是 \(0\) 连着 \(n\) 个长度为 \(m\) 的链。
这个游走的过程是比较简单的,其实就是一个依次标记叶子的过程,首先第一个叶子被标记,然后再是第二个,等等。在这个过程中,我们行走的始终是一些长度为 \(m\) 的完整的链,考虑从每条链和链的组合上分析这个问题。
考虑单独看一个根走到叶子这样的过程,设 \(f_i\) 表示走到 \(i\) 的期望步数,那么 \(f_{i}=\frac{1}{2}(f_{i-1}+f_{i+1})+1,f_0=0,f_{m}=f_{m-1}+1\)。然后这个就可以解,具体地,\(f_i-f_{i-1}=f_{i+1}-f_i+2\),然后 \(f_{m}-f_{m-1}=1\),所以 \(f_{m-1}-f_{m-2}=3,\dots,f_{1}-f_{0}=2m-1\)。再把这个差分前缀和回去,得到 \(f_{m}=\sum_{i=1}^{m}(2i-1)=2\sum_{i=1}^m i-m=m(m+1)-m=m^2\)。
然后再考虑不同叶子之间的组合,对于初始情况,有 \(\frac{n}{n}\) 的概率选中一条新链,第二次能用的链便只有 \(n-1\) 条,所以概率变为了 \(\frac{n}{n-1}\),期望变为了 \(\frac{n-1}{n}\),然后以此类推,选链的期望次数为 \(\sum_{i=1}^n \frac{n}{i}\),走链的次数就是 \(2\sum_{i=1}^n \frac{n}{i}-1\),每条链地位相同,这个东西乘上每条链的期望步数 \(m^2\) 就是答案。
这为什么是对的?比如说,某次游走的时候离开了这条链去了另一条链并走完了,那这个期望还和独立考虑一条链的期望一样吗?其实是一样的,因为你这样在其他链上游走的过程可以视为在这条链上折返。
[ABC337G] Tree Inversion
\(w\) 在路径中间,考虑枚举 \(w\) 并依次插入同时计算两边贡献,那么先前插入的所有点都能作为 \(v\) 对 \(w\) 做贡献,考虑贡献的形式有几种:若 \(v\) 在 \(w\) 子树外,想要经过 \(w\) 必须到 \(w\) 的子树内,对 \(w\) 的整个子树都有贡献;若 \(v\) 在 \(w\) 子树内,可以经过 \(w\) 到 \(w\) 的子树外,即对整棵树除 \(w\) 子树外的部分均有贡献;也可以经过 \(w\) 返回另一个子树,这里要住意没有贡献的不是这个点 \(v\) 的子树,而是 \(w\) 在 \(v\) 方向的儿子的子树,所以直接枚举复杂度就是对的,因为两个 \(w\) 的孩子的子树内的点路径不经过 \(w\),总之是贡献到 \(w\) 除 \(v\) 方向儿子的子树。
每次只操作一个区间,用 dfs 序维护,查询放在最后,这部分可以用差分,查询子树内关键点的个数可以用个 BIT 维护。
[ABC302Ex] Ball Collector
从两个中选一个的问题考虑到图论建模。考虑一个固定的 \(v\) 怎么做,将这条路径上所有的 \(a,b\) 连边后相当于对于每条边,恰要选择它的一个端点,问最多能选择几个端点。
这张图由若干个连通块组成:孤立点没有贡献;树只有 \(x-1\) 条边,故只能选择 \(x-1\) 个点,若有 \(\ge x\) 条边,因为只有 \(x\) 点,所以能选 \(x\) 个,容易发现这个上界只要有环就能达到。并查集维护即可,可以用一个数组统计合并两个相邻的点的次数。
现在要问所有的,考虑 dfs 这棵树每次跑完子树后撤销这个点的影响,使用可撤销并查集即可。
[ABC218G] Game on Tree 2
这没理由不会啊?
有这么一种博弈论题,不需要你去分析出所有的策略,这种问题在树上比较多见。只需要考虑当前的决策并由子树转移即可。
这个问题中答案只取决于最后到了哪个叶子,所以算出每个叶子的路径上的中位数,然后向上更新即可。
关于中位数,常见的求法一种是对顶堆,当每次的变化量较小时使用,流程是用一个大根堆维护前 $ \lceil \frac{n}{2} \rceil$ 小的,剩下的放到一个小根堆里,每次先根据大小关系判断这个数应该放在哪边,然后再动态调整两边的大小,如果有删除就换成 multiset;另一种是二分一个 \(mid\),把 \(<mid\) 的数设为 \(-1\),\(\ge mid\) 的数设为 \(1\),如果和大于 \(0\) 就可行,注意这里中位数的定义是第 \(\lceil \frac{n}{2}\rceil\) 个位置上的数。
考虑每个点是谁走取决于深度,所以每个点是先手还是后手是已经确定了的。所以直接记 \(f_x\) 表示 \(x\) 子树的答案。那么 A 就是取子树 \(\max\),B 就是取子树 \(\min\)。
[ABC342F] Black Jack
后手的操作与先手无关,所以分开考虑。先看后手的操作,直接设 \(g_i\) 表示他最后拿的数是 \(i\) 的概率,初始时 \(g_0=1\)。然后考虑掷一次骰子,可能到 \([1,d]\) 中的任意一个数,即 \(\frac{1}{d}g_{i} \to g_{i+j}\)。注意他是如果 \(<l\) 就继续,\(\ge l\) 就停止,因此我们只用 \([0,l)\) 的向后更新,然后在把这些点的 \(g\) 设成 \(0\) 即可。然后求出 \(g\) 的前缀和 \(s\)。
然后,再设一个 \(f_i\) 表示先手拿着 \(i\) 赢的概率。可以选择停下来,也可以选择继续掷骰子。如果停下来,有 \(1-s_n\) 的概率后手直接摇到 \(>n\) 输了,否则后手必须摇到小于 \(i\) 的数,概率为 \(s_{i-1}\);如果不停,那么 \(f_i=\frac{1}{d}\sum_{j=1}^d f_{i+j}\),两者取 \(\max\) 即可,最终答案为 \(f_0\),注意要倒着算。通过差分即可做到线性。
第一个 dp 比较自然,第二个 dp 其实省略了 \(f_i=0(i>n)\) 这一初值。
[ABC342G] Retroactive Range Chmax
一个想法是因为查询是单点的,我们可以维护所有修改,每次查询 \(x\) 就找到包含 \(x\) 的所有区间,取这些修改的最大值即可。
包含 \(x\) 的区间怎么找呢?可以建出线段树,把原先的区间修改挂在线段树上,用 multiset 维护插入删除。那么包含 \(x\) 的所有区间就是根到 \(x\) 的路径。
P2839 [国家集训队] middle
又是个中位数题,考虑二分。小于的设 \(-1\) 大于等于的设 \(1\),那么只需要看左端点在 \([a,b]\),右端点在 \([c,d]\) 的最大的一个和是不是 \(\ge 0\) 即可,注意它的下取整是下标从 \(0\) 开始。
因为 \(a<b<c<d\),所以 \([b,c]\) 间的数一定都选,\([a,b)\) 要选一段最大后缀,\((c,d]\) 要选一段最大前缀,这些用线段树都好维护,唯一的问题是每次都重新更新序列复杂度是 \(\mathcal{O}(qn\log^2 n)\)。
考虑一个数只会由 \(-1\) 变成 \(1\) 一次,当 \(mid\) 增大一的时候只有一点位置会变化,如果对于每个可能的 \(mid\) 用主席树维护出这个时候每个数的状态,均摊下来就是对的了。但是 \(mid\) 在值域级别,可以使用离散化压缩,因为最终的答案一定在序列中,不妨直接不考虑其他的数,直接二分答案在原序列中的排名即可,这样复杂度 \(\mathcal{O}(q \log^2n)\)。
实现的时候,主席树空间要开大一点,因为每次增加的点数并不是严格的 \(\lceil \log_2 n \rceil\),然后这里的主席树会在一个版本上多次修改,先把根复制一遍,然后每次新建节点先复制信息,然后把当前节点直接换成新建的节点,就是说要传地址。
[ABC287F] Components
肯定要树形背包,设 \(f_{x,j}\) 表示 \(x\) 的子树中共有 \(j\) 个连通块,但是涉及到选点方案这样还不足以转移。所以设 \(f_{x,j,0/1}\) 表示 \(x\) 的子树中共有 \(j\) 个连通块,\(x\) 选没选的方案数,那么答案是 \(f_{1,i,0}+f_{1,i,1}\)。
然后是一些树形背包的常用套路,首先沿着子树依次更新,就是用 \(f_{x,j}\) 和 \(f_{y,k}\) 更新 \(f_{x,j+k}\),\(j\) 枚举到 \(siz_x\),\(k\) 枚举到 \(siz_y\),复杂度是 \(\mathcal{O}(n^2)\)。先复制一份之前的 \(f_x\) 防止重复更新,顺便清掉之前的 \(f_x\)。
具体的转移是 \(f_{x,j+k,0} \leftarrow f_{x,j,0} \times (f_{y,k,0}+f_{y,k,1})\),因为 \(x\) 不选两部分肯定连不起来;\(f_{x,j+k,1} \leftarrow f_{x,j,1} \times f_{y,k,0},f_{x,j+k-1,1} \leftarrow f_{y,k,1}\),因为如果 \(x,y\) 都选会合并掉一个连通块。
细节:初始时仅有 \(f_{x,0,0}=f_{x,1,1}=1\),后面的会逐步更新,只需要考虑 \(x\) 自己。\(j,k\) 都可以取到 \(0\),只要一个点不选即可,因此对于 \(f_{?,v,1}\) 的转移,必须满足 \(v \ge 1\)。
[ABC287G] Balance Update Query
考虑选的时候肯定是从 \(a\) 值从大往小选,那么用动态开点权值线段树维护每个值,记下数值在一个区间的数的数量和这些数的和,修改很好做,查询就线段树上二分即可,注意 \(a_i \ge \color{red }0\)。
[ABC287Ex] Directed Graph and Query
考虑 Floyd 算法的 \(k\) 意味着只用了编号在前 \(k\) 个点做中转点的答案,那么你直接 Floyd 一遍找到每组询问第一次联通的那个 \(k\) 即可,显然它就是最小的。枚举 \(k\) 再枚举询问,复杂度可以接受。Floyd 做连通性可以用 bitset 优化。即记 \(f_i\) 表示与 \(i\) 联通的所有点的集合。枚举完 \(i,k\) 后如果 \(i,k\) 联通,那么 \(i\) 就可以经过 \(k\) 到达 \(k\) 联通的所有点,即 \(f_i \leftarrow f_i \operatorname{OR} f_j\)
[ABC234D] Prefix K-th Max
大脑缺失。
用优先队列动态维护前 \(k\) 大的数,那么答案就是堆顶。
[ABC234F] Reordering
大脑缺失 \(2\)。
答案跟原字符串没啥关系,只跟每种字符的出现数量有关系。设 \(f_{i,j}\) 表示前 \(i\) 种字符长度为 \(j\) 的方案数,转移就枚举这个字符选了几个即可,注意乘上组合数的系数。
[ABC234G] Divide a Sequence
首先 \(f_i=\sum_{j=0}^{i-1} f_j \max_{k \in (j,i]}a_k-\sum_{j=0}^{i-1} f_j\min_{k \in (j,i]}a_k\)。两部分结构一样,只讨论 \(\max\)。
如果倒着枚举 \(i\),那么后面是递增的,每个变化的位置是 \(a_i\) 左边第一个 \(>a_i\) 的位置,考虑同时求出 \(f\) 的前缀和,那可以一次一次跳依次求出每一段的和。
整个过程中和 \(i\) 有关的其实只有第一次跳的时候以 \(a_i\) 为最大值,假设跳到了 \(x\),那么接下来要求的就是 $\sum_{j=0}^{x-1} f_j \max_{k \in (j,x]} a_k$。这个其实可以直接用一个数组 \(mx\) 存起来。即 \(mx_i=\sum_{j=0}^{i-1} f_j \max_{k \in (j,i]}a_k\),那么 \(mx_i=mx_{x}+\sum_{j=x}^{i-1} f_j \times a_i\)。
在优化 dp 的时候要看一下和当前位置有关的东西到底有多少,同时考虑把和 \(i\) 无关的东西存起来。
[ARC134C] The Majority
\(1\) 要多于其它的所有数这个限制不好做 dp,考虑把 \(1\) 和其它的每个数配对,即我如果放了一个不为 \(1\) 的数,就要放一个与它配对的 \(1\)。又因为是严格大于,要给每个盒子先放上一个 \(1\)。如果用掉这些 \(1\) 后 \(a_1\) 仍然非负就有解。答案等价于这 \(n\) 个球随便放到 \(k\) 个盒子里的方案数,盒子可以为空,方案数为 \(\binom{a_i+k-1}{k-1}\),乘起来即可。
P8327 [COCI2021-2022#5] Radio
只需考虑质因子的情况,如果一个质因子在区间中出现了 $>1 $ 次就不行了。
所有数都只出现了 \(\le 1\) 次意味着没有重复数字。直接把每个 \(a_i\) 丢掉,塞回去 \(a_i\) 的质因子集合,不关注数量则理论最多只有 \(7n\) 个数,实测则为 \(2853708\),这样等价于问一个区间内有没有重复数字。
直接上线段树和 set,每次更新会更新 \(10\) 个左右的数,修改的是这个数存在不存在,不存在把它改成 \(0\),同时对于值为 \(0\) 的数,强制令它的 \(pre=0\) 即可。
CF486D Valid Sets
考虑一棵树的联通子图数量怎么求。设 \(f_x\) 表示包含 \(x\) 的联通子图数量,则 \(f_x =\prod_{y \in \operatorname{Son(x)}} (f_y+1)\)。
可以 \(n^2\),枚举一个 \(x\),强制 \(x\) 为连通块中的最小值,那么 dfs 即可,遇到一个减去它大于 \(k\) 或小于它的就返回,跑上面那个 dp。但是对于相同点权会算重,考虑加上编号作为第二关键字即可。
CF486E LIS of Sequence
求出以 \(i\) 结尾和开头的 LIS 长度,记为 \(f_i,g_i\)。那么可能在 LIS 中就是 \(f_i+g_i-1=L\),其中 \(L\) 为 LIS 长度。然后经典结论是,\(f_i+g_i-1\) 代表的 LIS,\(i\) 这个数一定在 \(f_i\) 位,所以可能在 LIS 中的点如果有两个相同的 \(f_i\) 值,那么它们都能做 LIS 的第 \(f_i\) 位,也就都不一定在 LIS 中。
[ABC117C] Streamline
贪心会不了一点。
要求 \(x\) 递增,每个人只能向右走,这些假设显然不影响答案。如果只有一个人,要走的距离是 \(x_m-x_1\),这个距离是有所有的 \([x_i,x_{i+1})\) 这样的小段组成的。那么考虑如果想减少距离就要剪掉一些这样的小段,把这些小段从大到小排序,剪掉前 \(n-1\) 大的。
[ABC165D] Floor Function*
整理:关于下取整函数的一些性质。A Link.
\(\lceil \frac{n}{m} \rceil =\lfloor \frac{n-1}{m} \rfloor +1\),证明可以讨论能不能整除。
\(\lfloor \frac{a}{nm} \rfloor=\lfloor \frac{\frac{a}{n}}{m}\rfloor=\lfloor \frac{\lfloor \frac{a}{n}\rfloor+(\frac{a}{n}-\lfloor \frac{a}{n}\rfloor) }{m}\rfloor =\lfloor \frac{\lfloor \frac{a}{n}\rfloor}{m}\rfloor\),原因是小于一的部分直接被消去了。
\(A,B,x \in \mathbb{Z},\lfloor \frac{Ax}{B} \rfloor=\lfloor A(\frac{x}{B}-\lfloor \frac{x}{B} \rfloor +\lfloor \frac{x}{B}\rfloor)\rfloor=\lfloor A(\frac{x}{B}-\lfloor \frac{x}{B} \rfloor)\rfloor+A\lfloor \frac{x}{B}\rfloor\),原因是整数可以直接从取整里提出来。
因此这道题的式子就转化为了 \(\lfloor A(\frac{x}{B}-\lfloor \frac{x}{B} \rfloor)\rfloor\) 的最大值,那么我们要最大化 \(\frac{x}{B}-\lfloor \frac{x}{B} \rfloor\),考虑在让 \(x\) 对后面没有贡献的同时尽量大,因此取 \(B-1\),如果取不了,那还是尽量大,取 \(N\) 即可。
[ABC162F] Select Half
设 \(f_i\) 表示前 \(i\) 个数选了 $\lfloor \frac{i}{2} \rfloor $ 个数的答案。
- 若 \(i\) 是偶数
- 若 \(a_i\) 不选,那么想选一半必须选 \(i-1,i-3,\dots\) 这样。
- 若 \(a_i\) 选,则 \(i-1\) 不能选,前面要再选出一半的数。
- 若 \(i\) 是奇数
- 若 \(a_i\) 不选,前 \(i-1\) 个数里要选出一半的数。
- 若 \(a_i\) 选,则 \(i-1\) 不能选,前面要再选出一半的数。
注意 \(f_1=0\)。

浙公网安备 33010602011771号