2024.7.31
\(100+40+1+20\),\(\text{T2}\) 太扯了。
T1 黑客
别人都觉得是送分,但为什么我的思路使这个题有绿以上的难度,且实现还相当难调……暴力分先拿上,然后发现每次贡献答案的范围非常小,考虑枚举,即使是 \(n^3\) 也不会爆,那么枚举互质的分子分母。接下来在两个序列中找到符合比例关系的第一项,然后在两个序列里算出总贡献,累计即可。还可分治一下,每次只枚举那个小的分子/分母。调调乱了重构,直接过大样例。
发现 \(\text{round}\) 函数出锅了,忘了咋用,只能手写一个向上取整。花了俩小时才干掉,太废了。这个做法就挺跟人不一样的。
T2 密码技术
第一题耗太久,就想着后面纯暴力走掉算了,没想到 \(\text{T2}\) 还是个送分,气死。
哪些行和列分别是能换的肯定是已知的,发现行和列互不影响,所以可以分开分析,最后行列方案数分别计算后乘起来就是答案。考虑这样一件事,\(a\) 和 \(b\) 能够互换,\(b\) 和 \(c\) 能够互换,那么 \(b\) 和 \(c\) 就可以互换,相当于三者构成了一个连通块,那么方案数即排列方案就是连通块大小的阶乘,分别用并查集维护行和列的连通块,算出每个连通块大小的阶乘即可。预处理出阶乘之后每个点都可以在 \(2ms\) 内通过。
唐
T3 修水管
黄黄紫紫,标准的提高组模拟搭配。
这种 \(\text{dp}\) 是真的推不出来……
读题都读不太明白别说推式子了,暴力都不知道咋打才能对。
题目其实钦定修复一定漏水,漏水一定修复,不漏水就不修复。还说了修复后就不漏水了,其实就是在说每个位置只用修一次……语文太差导致的。
其实读懂了也没那么难。对于第一个口,它漏水的概率是 \(p_1\),修复 \(r\) 轮后还不漏的概率是 \((1-p_1)^r\),那漏水的概率是 \(1-(1-p_1)^r\)。那么我们设 \(f_{i,j}\) 表示前 \(i\) 个位置,在 \(r\) 轮中修复 \(j\) 次的概率,枚举每一个位置是被修过了还是从来没爆。
这里枚举每一轮,计算每一个位置修复的总概率 \(P_i\),即前 \(i-1\) 个口在 \(r\) 轮中爆了 \(j\) 个且修掉的概率乘上第 \(i\) 个口在剩下的 \(r-j\) 次机会中爆了并修好的概率。
考虑 \(f_{i,j}\) 的转移:
-
从 \(f_{i-1,j}\) 转移过来,即第 \(i\) 个口在剩下的 \(r-j\) 次爆的机会中都没有爆。
\[f_{i,j} += f_{i-1,j}\cdot (1-p_i)^{r-j}(i>0) \] -
从 \(f_{i-1,j-1}\) 转移过来,即第 \(i\) 个口在第 \(j\) 次漏水机会中爆了,说明它在前 \(j-1\) 次爆炸机会中都没爆,那么就会在后 \(r-(j-1) = r-j+1\) 次机会中爆一次。这里再强调一下,在后 \(r-j+1\) 次机会中一直没爆的概率是 \((1-p_i)^{r-j+1}\),那么用 \(1\) 减掉就是爆了的概率。
\[f_{i,j} += f_{i-1,j-1}\cdot (1-(1-p_i)^{r-j+1})(i,j>0) \]
最终答案汇总到 \(f_{n,r}\) 停止。
然后就没了,所有的 \(P_i\) 和 \(d_i\) 都知道,期望就能算了。
总结:要从总体出发。每一轮的贡献必定不能纳入转移,不然会有后效性,可以纳入的是漏水次数,而不是轮数。知道总漏水次数便可以计算概率了,而轮的话,有些轮还不一定漏。
T4 货物搬运
这题是一眼数列分块,比较常规的做法就是分块之后用双端队列维护块首位,然后暴力移动左右散块,\(O(n\sqrt{n})\)。
或者就是平衡树维护序列总移动,这是一个平衡树的常规操作,\(O(\log n)\) 维护序列插入修改移动等操作,又因为 \(\forall a_i \le n\),所以再开 \(n\) 棵小树维护每钟 \(a_i\) 的编号然后查询编号在 \([l,r]\) 之间的个数即可。
说一个比较卡常的做法。考虑优化暴力,可以以 \(\frac{n}{2}\) 为阈值分治查询。由于序列中每种数字的总和一定,所以序列中的总答案是一定的。
- 当 \(r-l+1 \le \frac{n}{2}\) 时,暴力查询。
- 当 \(r-l+1 > \frac{n}{2}\) 时,暴力查询 \([1,l-1] ,[r+1,n]\),用序列中总答案减去该答案即可。
复杂度玄学,但是能随便过学校 \(\text{OJ}\) 的点。我怀疑学校的水点以后凡是区间问题都能用类似这种方法分治搞过去。
这样硬搞肯定是过不了 \(\text{CF}\) 原题的。
但是可以加入如下科技:
memmove
函数。实现数组区间赋值,可以用它来解决区间覆盖的赋值。如果使用memcpy
,无法覆盖原有数值,二者使用方法无区别。- 将 \(a_i\) 分成两部分。发现我们的常数主要在
memmove
和比较可以优化。而数字的位数越小,比较越快,修改越快。不妨把 \(a_i\) 拆成后 \(8\) 位和前 \(9\) 位(\(a_i \le 10^5\)),分别用unsigned char
(\(8\) 位) 和short
(\(16\) 位)来存。