牛客寒假基础算法训练营4(ADFGJL)

A

期望计算 + 推公式

首先,由期望的线性性可知和式的期望等于和式中每一项的期望之和。因此求i=2n|a[i1]a[i]|的期望,等价于求每一个|a[i1]a[i]|的期望,再求和。所以问题转化为求|a[i1]a[i]|的期望,其中a[i][li,ri]内等概率取值。

易得:

E(X)=ai1=li1ri1ai=liri|a[i1]a[i]|(ri1li1+1)(rili+1)

考虑怎样用一个关于li1,li,ri1,ri的公式直接得到分子的值:

两个区间的交并关系只有三种:

  1. 相互分离 -> 这个容易得到
  2. 相互包含 -> 可以拆为 两对相互分离 和 一对相同线段重合
  3. 相交 -> 也可以拆为 两对相互分离 和 一对相同线段重合

于是等价于求 两条线段相互分离 和 两条相同的线段重合 这两种情况的分子和式,通过推公式可以得到解。具体见代码。

code

D

思维题 + 贪心

首先短字符串中能与长字符串中字符匹配就要尽可能贪心匹配,这样能保证答案不会更差。

对于匹配上的一对字符,可以作为回文串的外侧。这样就只需要考虑回文串内部的匹配即可。

这样问题就变为:给定一个字符集合,每次操作可以修改一个字符,使得该字符集合可形成回文串的最小修改次数。

考虑回文串:长度为偶数时,字符集合可形成回文串的充要条件为所有字符数量均为偶数;同理长度为偶数时,充要条件为只有一种字符数量为奇数,其余均为偶数。

可见需要尽可能减少奇数数量字符的种类数。最优方案即为每次修改一个奇数字符为另一个奇数字符,这样每一次操作都可以将两种奇数字符变为两种偶数字符,贪心操作即可。

code

F

二分 + 双指针

求第 k 大可以二分来求——看(a[i]+a[j])modp>=mid 的数量是否 >=k 即可。乍一看有模 p,序列内的单调性不确定。实际上将每个 a[i]均模 p后,(a[i]+a[j])modp>=mid 的情况只有两种:

  1. mid<=a[i]+a[j]<=p1
  2. p+mid<=a[i]+a[j]<=2p1

这是由模 pa[i]+a[j]<=2p 这一关键性质决定的。因此将序列模 p 后再升序排序,对于每个 a[i],通过两次二分就可以直接确定(a[i]+a[j])modp>=mid(i,j)对数量,进而以 O(nlognlogV)的复杂度确定第 k 大。

找到 (a[i]+a[j])modp 的第 k 大后,前 k1 大的数量不会超过 k,可以直接暴力求得。

注意不能直接把所有前 k 大求出,因为数量并不确定(只知道第 k 大,并不知道第 k 大有多少个,可能 >=k 的数量是 O(n2)级别的),而要先将所有 k1 大求出(能保证它们的数量是 <k的),额外记录第 k 大的数量,再看总数是否超过 k 即可。这样能保证复杂度控制在 O(k),具体细节见代码。

code

G

主席树

这里换一个与 F 不同的思路:即用堆维护每个 i,当前未输出的最大值,这样就能保证每一次堆顶一定是当前所需要的最大值。

则问题转化为:求对某个 i,且 j[li,ri](a[i]+a[j]) mod p 未输出过的最大值。

(a[i]+a[j]) mod p<=2p 这一关键性质,可以发现只需要找以下二者的最大值:

  1. <pa[i] 的最大值
  2. >=pa[i]<2p 的最大值,即整体的最大值

求2很简单,只需要求区间最大值即可。而求1可以用主席树——先查询 a[li,ri][1,pa[i]1] 的数量,即 pa[i]1 在其中的排名,设为r,则 mx1 即为 rth。求mx2,即查询区间最大值,也可以转化为求主席树的第 k 小。类似于归并排序的思想,可以动态维护好对于某个 i 的前 x 大值,所有 i 用一个大根堆维护即可。具体细节见代码。

code

J

一道思路很好想但代码较难写的图论题

处理出每个联通块的 size 和包含的最小结点编号,根据 k 与联通块个数 cntg 的大小关系,分两种情况讨论:

  1. k<=cntg:部署数不充足,只能挑选前 k 大的联通块部署。为保证字典序最小,每个联通块部署的肯定是编号最小的结点,然后可以先处理出每个联通块的字典序最小方案(bfs + 优先队列),再用优先列处理每个联通块的方案,保证最终构造序列的字典序最小即可。这是我的做法,比较麻烦,实际上不需要处理每个联通块的字典序最小方案,将所有初始点放进一个优先队列里,同时对多个联通块做一次BFS即可。

  2. k>cntg:部署数有剩余,显然可以部署到所有联通块,但盈余的部署数需要服务于字典序最小。可以贪心地考虑:在剩余部署数>未部署的联通块个数时,可以直接部署当前未在序列中的编号最小的结点,当然如果可以通过在当前序列中的点直达,就不需要浪费部署次数,直接加入序列即可。从now=1开始贪心地按顺序考虑,直到剩余部署次数=未部署联通块个数时,某个前缀1now一定是序列的前缀。再对now+1n继续采用1的思路即可。(代码写得比较麻烦qwq...)

code

L

典题

题目:对于两个数组[a,b],要做到给定 查询区间[l,r],快速求出:

i=lrj=ira[i]b[j]

这种题首先一定要想到拆位,将每个二进制位的贡献分开计算,最后求和。

如果题目改为求:

i=lrj=lra[i]b[j]

那么问题就变得很简单了—— 对于第i个二进制位,贡献只来自于不同数字(01)相异或。所以该位的贡献为:

2icnt1cnt0

其中cnt1a[lr]内第i位为1的元素个数,证明略。

而两式区别在于j=l改为了j=i,那么该怎么做呢?

可以O(n)预处理出每个后缀的答案(即固定 r=n,对每个 l 求一遍答案),设为sufans[l]

那么对于[l,r]ans=

sufans[l]sufans[r+1]j=030(numa0numb1+numa1numb0)

numa1a[l,r]中第 j 位为 1 的元素个数
numa0a[l,r]中第 j 位为 0 的元素个数
numb1b[r+1,n]中第 j 位为 1 的元素个数
numb0b[r+1,n]中第 j 位为 0 的元素个数

证明:模拟一下即可

code

posted @   jxs123  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示
主题色彩