牛客寒假基础算法训练营4(ADFGJL)
A
期望计算 + 推公式
首先,由期望的线性性可知和式的期望等于和式中每一项的期望之和。因此求\(\sum_{i=2}^{n}|a[i-1]-a[i]|\)的期望,等价于求每一个\(|a[i-1]-a[i]|\)的期望,再求和。所以问题转化为求\(|a[i-1]-a[i]|\)的期望,其中\(a[i]\)在\([l_{i},r_{i}]\)内等概率取值。
易得:
考虑怎样用一个关于\(l_{i-1},l_{i},r_{i-1},r_{i}\)的公式直接得到分子的值:
两个区间的交并关系只有三种:
- 相互分离 -> 这个容易得到
- 相互包含 -> 可以拆为 两对相互分离 和 一对相同线段重合
- 相交 -> 也可以拆为 两对相互分离 和 一对相同线段重合
于是等价于求 两条线段相互分离 和 两条相同的线段重合 这两种情况的分子和式,通过推公式可以得到解。具体见代码。
D
思维题 + 贪心
首先短字符串中能与长字符串中字符匹配就要尽可能贪心匹配,这样能保证答案不会更差。
对于匹配上的一对字符,可以作为回文串的外侧。这样就只需要考虑回文串内部的匹配即可。
这样问题就变为:给定一个字符集合,每次操作可以修改一个字符,使得该字符集合可形成回文串的最小修改次数。
考虑回文串:长度为偶数时,字符集合可形成回文串的充要条件为所有字符数量均为偶数;同理长度为偶数时,充要条件为只有一种字符数量为奇数,其余均为偶数。
可见需要尽可能减少奇数数量字符的种类数。最优方案即为每次修改一个奇数字符为另一个奇数字符,这样每一次操作都可以将两种奇数字符变为两种偶数字符,贪心操作即可。
F
二分 + 双指针
求第 \(k\) 大可以二分来求——看\((a[i] + a[j]) mod p >= mid\) 的数量是否 \(>=k\) 即可。乍一看有模 \(p\),序列内的单调性不确定。实际上将每个 \(a[i]\)均模 \(p\)后,\((a[i] + a[j]) mod p >= mid\) 的情况只有两种:
- \(mid <= a[i] + a[j] <= p - 1\)
- \(p + mid <= a[i] + a[j] <= 2 p - 1\)
这是由模 \(p\) 后 \(a[i] + a[j] <= 2p\) 这一关键性质决定的。因此将序列模 \(p\) 后再升序排序,对于每个 \(a[i]\),通过两次二分就可以直接确定\((a[i] + a[j]) mod p >= mid\) 的 \((i, j)\)对数量,进而以 \(O(nlognlogV)\)的复杂度确定第 \(k\) 大。
找到 \((a[i] + a[j]) mod p\) 的第 \(k\) 大后,前 \(k-1\) 大的数量不会超过 \(k\),可以直接暴力求得。
注意不能直接把所有前 \(k\) 大求出,因为数量并不确定(只知道第 \(k\) 大,并不知道第 \(k\) 大有多少个,可能 \(>=k\) 的数量是 \(O(n^{2})\)级别的),而要先将所有 \(k-1\) 大求出(能保证它们的数量是 \(<k\)的),额外记录第 \(k\) 大的数量,再看总数是否超过 \(k\) 即可。这样能保证复杂度控制在 \(O(k)\),具体细节见代码。
G
主席树
这里换一个与 \(F\) 不同的思路:即用堆维护每个 \(i\),当前未输出的最大值,这样就能保证每一次堆顶一定是当前所需要的最大值。
则问题转化为:求对某个 \(i\),且 \(j \in [l_{i}, r_{i}]\),\((a[i] + a[j]) \space mod \space p\) 未输出过的最大值。
由 \((a[i] + a[j]) \space mod \space p <= 2p\) 这一关键性质,可以发现只需要找以下二者的最大值:
- \(<p-a[i]\) 的最大值
- \(>=p-a[i],<2p\) 的最大值,即整体的最大值
求2很简单,只需要求区间最大值即可。而求1可以用主席树——先查询 \(a[l_{i},r_{i}]\) 内 \([1,p-a[i]-1]\) 的数量,即 \(p-a[i]-1\) 在其中的排名,设为\(r\),则 \(mx1\) 即为 \(rth\)。求\(mx2\),即查询区间最大值,也可以转化为求主席树的第 \(k\) 小。类似于归并排序的思想,可以动态维护好对于某个 \(i\) 的前 \(x\) 大值,所有 \(i\) 用一个大根堆维护即可。具体细节见代码。
J
一道思路很好想但代码较难写的图论题
处理出每个联通块的 \(size\) 和包含的最小结点编号,根据 \(k\) 与联通块个数 \(cntg\) 的大小关系,分两种情况讨论:
-
\(k<=cntg\):部署数不充足,只能挑选前 \(k\) 大的联通块部署。为保证字典序最小,每个联通块部署的肯定是编号最小的结点,然后可以先处理出每个联通块的字典序最小方案(\(bfs\) + 优先队列),再用优先列处理每个联通块的方案,保证最终构造序列的字典序最小即可。这是我的做法,比较麻烦,实际上不需要处理每个联通块的字典序最小方案,将所有初始点放进一个优先队列里,同时对多个联通块做一次\(BFS\)即可。
-
\(k>cntg\):部署数有剩余,显然可以部署到所有联通块,但盈余的部署数需要服务于字典序最小。可以贪心地考虑:在剩余部署数\(>\)未部署的联通块个数时,可以直接部署当前未在序列中的编号最小的结点,当然如果可以通过在当前序列中的点直达,就不需要浪费部署次数,直接加入序列即可。从\(now=1\)开始贪心地按顺序考虑,直到剩余部署次数\(=\)未部署联通块个数时,某个前缀\(1到now\)一定是序列的前缀。再对\(now+1到n\)继续采用1的思路即可。(代码写得比较麻烦qwq...)
L
典题
题目:对于两个数组\([a,b]\),要做到给定\(\forall\) 查询区间\([l,r]\),快速求出:
这种题首先一定要想到拆位,将每个二进制位的贡献分开计算,最后求和。
如果题目改为求:
那么问题就变得很简单了—— 对于第\(i\)个二进制位,贡献只来自于不同数字\((0和1)\)相异或。所以该位的贡献为:
其中\(cnt1\)指\(a[l到r]\)内第\(i\)位为\(1\)的元素个数,证明略。
而两式区别在于\(j=l\)改为了\(j=i\),那么该怎么做呢?
可以\(O(n)\)预处理出每个后缀的答案(即固定 \(r=n\),对每个 \(l\) 求一遍答案),设为\(sufans[l]\)。
那么对于\([l,r]\),\(ans=\)
\(numa1\):\(a[l,r]\)中第 \(j\) 位为 \(1\) 的元素个数
\(numa0\):\(a[l,r]\)中第 \(j\) 位为 \(0\) 的元素个数
\(numb1\):\(b[r+1,n]\)中第 \(j\) 位为 \(1\) 的元素个数
\(numb0\):\(b[r+1,n]\)中第 \(j\) 位为 \(0\) 的元素个数
证明:模拟一下即可

浙公网安备 33010602011771号