Cry_For_theMoon  

Codeforces Round #773 (Div.1 )A~D 题解

比赛地址

下大分。

A. Great Sequence

给定正整数 \(x\)。称一个长度为 \(n\) 的序列是好的,当且仅当 \(n\) 是偶数,且可以将 \(a\) 重新排列,使得 \(\forall i,a_{2i-1}\cdot x=a_{2i}\)

给出一个长度为 \(n\) 的数组 \(x\),试求至少要添加几个数,使得 \(a\) 序列是好的。

\(n\le 2\times 10^5,2\le x\le 10^6,1\le a_i\le 10^9\)

分析:

我们要让 \(a\) 中的数尽可能多的两两配对,满足每一对的大数都是小数的 \(x\) 倍。答案是未能成功配对的数的个数。

从小到大枚举 \(a\) 中的数,每次枚举到一个未配对的数 \(v\),如果还有一个 \(v\cdot x\) 也没配对,就把他们都删去。否则答案加一。

这个贪心是对的,正确性不难证明。

时间复杂度 \(O(n\log n)\)

代码

B. Repetitions Decoding

称一个长度为 \(n\) 的序列是“重复的”,当且仅当 \(n\) 是偶数,且前一半等于偶一半。

称一个序列是好的,当且仅当它可以被划分成若干个“重复的”序列。

给定一个长度为 \(n\) 序列 \(a\),你可以进行最多 \(2n^2\) 次操作,每次操作在 \(a\) 的一个位置插入两个相同字符。目标是使得 \(a\) 变成好的序列。

如果无解输出 -1;否则先输出所有的操作,然后输出此时 \(a\) 的任意一种划分方案。注意不需要最小化答案。

分析:

构造是我短板实锤了。

误区是陷入类似分段 dp 的形式去探究把 \(a\) 中的一段变成重复的方案,因为这个东西一不好做,二是通过观察样例 \(2\) 我们注意到我们可以添加一串东西(比如 123321) 然后把一部分划分进左边的重复序列,另外一部分划分进右边的重复序列,这是不好研究和表示的。

需要注意到不能把每次操作添加的字符孤立考虑,因为我们可能会加出 \(11\rightarrow 1221 \rightarrow 122331\) 这样的东西。

那么首先一个事情是任意一个长度为偶数的回文串我们都能添加进去。然后有趣的是此时我们可以翻转 \(a\) 的任意一个子段 \([l,r]\):我们只要在 \(a_r\) 之后插入回文串 \(s_{l}...s_{r}s_{r}...s_{l}\),然后把两个挨在一起的 \(s_{l}...s_{r}\) 消去(指划分进一个“重复的”序列)即可。

那么这个问题就可做多了,因为串不会在变化得很厉害了。

套路地,我们思考什么时候无解,显然一个字符出现次数为奇数无解。根据直觉(套路),如果每个字符出现偶数按理来说必定有解的。每个字符出现次数为偶数,容易想到让同种字符之间两两配对。比如 (11)(11)(33)(44)(66) 这样的。换言之我们要让最后的 \(a\) 有序。

所以问题转变成了用区间任意翻转进行排序,这个时候做法就很多了。比较简单的是只利用前缀翻转,然后变成了经典入门题。容易分析出操作上界实质上是 \(O(n(n+1))\) 的。还有一种做法,也是递归的:我们考虑找到 \(a_1\) 的下一次出现位置 \(p\),然后把 \([2,p]\) 翻转,然后消去 \([a_1,a_2]\)。此时变成了子问题。都很好实现。

代码

C. Anonymity Is Important

\(n\) 个布尔变量,然后给出 \(q\) 次操作:

  • 0 l r v :表示第 \(l\sim r\) 个变量的位或和为 \(v(v\in [0,1])\)

  • 1 x :询问第 \(x\) 个布尔变量是否的值是否能被确定。如果能,输出它的值;否则输出 N/A 表示不能确定。

\(n,q\le 2\times 10^5\)

分析:

良心出题人放了我的 2log 过去。

良心有啥用,这题是 system test 刚结束就交秒过的...

我们意识到,难点在于判断一个变量的值到底是 \(1\) 还是不能确定。

如果 \(x\) 必须是 \(1\),那么存在一条线段 \([l,r]\) 覆盖 \(x\) 且下标在 \([l,x)\cup(x,r]\) 范围内的变量都是 \(0\)

我们考虑维护所有确定为 \(0\) 的变量构成的极长连续段。每次询问的时候,\(l=x\lor r=x\) 的情况是容易处理的,我们只关心是否存在 \(l\lt x\lt r\) 的线段满足 \([l,x)\cup(x,r]\) 范围内的变量都是 \(0\)

那首先 \(l-1,r+1\) 都得是确定为 \(0\)。考虑对每个极长连续段维护一个 \(set\),对于每个在这个段内的点 \(x\)\(set\) 中加入所有左端点为 \(x\) 的线段的右端点。那么我们每次就是关注 \(l-1\) 所属于的连续段,它维护的 \(set\) 内是否存在一个在 \(r+1\) 所属的连续段的元素。这可以通过简单的二分完成,不再赘述。

考虑合并极长连续段,可以维护并查集,按照 \(set\) 的大小进行启发式合并,根据按秩合并的理论复杂度应该是 \(O(q\log q)\) 的,每次合并都是 \(set\) 的插入删除,所以复杂度可以视作 \(O(n\log^2 n)\)

代码

D. Two Arrays

给定 \(n\) 个长度为 \(m\) 的数组 \(a_1,a_2,...,a_n\)。第 \(i\) 个数组有一个附加权值 \(j\)

定义 \(val(i,j)=w_i+w_j\),对于所有的满足数组 \(a_i,a_j\) 内的数字两两不同的 \(i,j\),求 \(val(i,j)\) 的最小值。如果不存在两个数组满足它们内的数字两两不同,输出 -1

\(n\le 10^5,m\le 5,1\le a_{i,j},w_i \le 10^9\)

分析:

每次 CF 写双指针必挂,必定是傻逼错误 debug 半天。

容易想到按照 \(w_i\) 排序,以下称 \((i,j) \mid i<j\) 合法当且仅当 \(a_i\)\(a_j\) 的数字两两不同。

首先对于每个 \(i\) 我们只关注最小的 \(j\) 满足 \((i,j)\) 合法,我们只关注这样的 \(val(i,j)\)

然后我们把 \((i,j)\) 看成一条覆盖 \([i,j]\) 的线段,发现如果两条线段相交但不是包含关系,则左端点靠左的线段显然更优秀。

容易想到 two-pointers,但一般的 two-pointers 解决的都是:如果线段 \(a\) 包含线段 \(b\),那么线段 \(a\) 一定比 \(b\) 更优秀。诸如这种形式的问题。我们需要思考一下怎么设计一个合理的双指针。

这个时候,我们应该从小到大枚举右端点,然后随着右端点的增大,左端点只能变得更小。

最后,我们需要解决的问题是,如何快速判断指针能否移动。右端点每次移动一个一格,但是我们不知道左端点要移动到多小才会停止。

事实上要做的是这个问题:判断是否存在一个 \(1\le i\le l\),满足 \((i,r)\) 合法。

发现 \(m\le 5\) 的优秀性质至今还没有用到。那么首先方向是考虑带 \(2^m\) 的东西 \(\rightarrow\) 暴力枚举 \(a_r\) 的所有子集。

首先,我们考虑判断 \((l,r)\) 是否合法,那我们可以把 \(a_l\)\(m\) 个数插入到 \(set\),然后枚举 \(a_r\)\(m\) 个数,看是不是在 \(set\) 里。这个方法单次的复杂度是优秀的,仅为 \(O(m\log m)\),但是我们发现这个方法并不能支持多个 \(l\) 同时和一个确定的 \(r\) 放在一起询问。

本题的牛逼之处来了:首先我们把 \(n\) 个数组分别排序。直接枚举 \(a_r\) 的所有子集,对于一个子集,设大小为 \(x\)。如果在 \(a_1\sim a_{l}\) 中这个子集出现了 \(y\) 次,则 \(ret\leftarrow ret+(-1)^{x}y\)。最后如果 \(ret\neq 0\) 说明存在一个 \(1\le i\le r\) 满足 \((i,r)\) 合法。否则说明不存在。

这个想到了以后就不难证明了:首先考虑就只是判断 \((i,j)\) 是否合法,套用这个方法是对的(如果交集为空应该是 \(1\) 否则是 \(0\))。多个的话,这个东西是可以累加的,所以可以把容斥的式子也拆分开来相加。

最后时间复杂度大概是 \(O(n\log n2^m)\)

代码

posted on 2022-02-25 20:16  Cry_For_theMoon  阅读(298)  评论(2)    收藏  举报