Codeforces Round #741 (Div. 2)
C - Rings
题意
给出一个二进制串 S,长度为 N,你可以在上面做 [l,r] 的截断,函数 f(l,r) 表示:将 S 中 [l,r] 的截断取出,然后转换为十进制的数。
要求找出两对不同的 (l1,r1),(l2,r2) 满足以下条件:
-
1⩽l1⩽r1⩽n,r1−l1+1⩾⌊2n⌋
-
1⩽l2⩽r2⩽n,r2−l2+1⩾⌊2n⌋
-
l1=l2 和 r1=r2 至少有一个满足。
-
存在一个非负整数 k,使得 f(l1,r1)=f(l2,r2)⋅k。
题目保证有解,任意给出一组解即可。
数据范围:2⩽N⩽2×104。
思路
不要把 k 想的很复杂,可以就考虑几个简单的,如 0,1,2 即可。
这个题就是构造出一种解就完事了,所以进行分类讨论。
- 如果 S 中所有字符都相等,则直接输出 [1,n−1],[2,n] 即可,因为他们直接相等。
由于题目要求取出的串长度大于等于 ⌊2n⌋,所以考虑从 S 串的 ⌊2n⌋ 位置作为截断,分左右进行讨论。
-
如果 S 的左半部分位置 x⩽⌊2n⌋ 处为 0,则输出 [x,n],[x+1,n],因为前导零去掉后,他们直接相等。
-
如果 S 的右半部分位置 x>⌊2n⌋ 处为 0,则输出 [1,x],[1,x−1],因为后置零,可以通过 ×2 得到,f(1,x)=f(1,x−1)⋅2。
以上就已经讨论完了所有可能情况了,按情况输出即可。
时间复杂度 O(T×N)。
点击显/隐代码
D - Two Hundred Twenty One
题意
给出一个长度为 N 的字符串 S 和 Q 次询问,每次询问一个区间 [li,ri]。
对于数列 a1,a2,…,an,定义“不同符号和”(sign-variable):
s({an})=a1−a2+a3−a4+⋯+(−1)n−1⋅an=∑i=1n(−1)i−1⋅ai
现在数列 a1,a2,…,an 只有 +1 和 −1 构成,通过只含有正负号的字符串 S 给出
如果 ai=1⇒Si=+,ai=−1⇒Si=-
每次询问给出区间 [li,ri],设字符串 T=SliSli+1⋯Sri,求至少要从 T 中删除多少个字符才能使得 s(T)=0,并给出每次删除的字符位置。(第一个问题是D1所要求的,第二个问题是D2所要求的)。
数据范围:1⩽N,Q⩽3×105。
思路
我们先不考虑 S 的子串问题,先考虑整个 S 串。
设 s(l,r)=∑i=lr(−1)i−1ai,表示 s(S) 中 [l,r] 这一段的不同符号和。
设 f(i) 为去掉 S 串中第 i 个字符后变为 S′,整个 S′ 的不同符号和,即 f(i)=s(S′)。
命题1:∣f(i)−f(i+1)∣=0或2
证明:
不难发现:
⇒f(i)=s(1,i−1)−s(i+1,n)f(i+1)=s(1,i)−s(i+2,n)∣f(i)−f(i+1)∣=∣s(i,i)+s(i+1,i+1)∣=∣ai−ai+1∣
则:
QED
下面这个结论不难证明:
命题2:∣S∣ 与 s(S) 同奇偶性,也就是字符串长度和字符串的不同符号和的奇偶性相同。
因为 +1 和 −1 之和会发生抵消,抵消以后字符总数 −2 不影响奇偶性,所以最终的不同符号和奇偶性与开始时的字符总长奇偶性相同。
所以,如果 s(S)=0 则一定有 ∣S∣ 为偶数。
命题3:当 s(1,n)=0 时,f(1)⋅f(n)⩽0。
证明:
命题等价于证明 f(1) 和 f(n) 异号,或者至少有一个为 0。
f(1)=−s(2,n)=a1−s(1,n)f(n)=s(1,n−1)=s(1,n)−an
由于 ∣s(1,n)∣⩾1,所以 f(1) 和 f(n) 的符号只能取决于 s(1,n) 的符号,a1,an 对它们的影响太小,而 s(1,n) 在 f(1),f(n) 中的符号正好是一正一负,所以 f(1),f(n) 只能异号,或者至少一个为 0。
QED
下面给出删除方法:
设 n=∣S∣ 为奇数,则 f(i) 为偶数,因为它从 S 中删除了一个字符。
又通过命题1知,相邻的 f(i),f(i+1) 之间差值为 0 或 2,由命题3知,f(1),f(n) 异号,则一定存在 k,使得 f(k)=0,可以通过反证法证明。(类似于连续函数的零点存在定理)
则说明,对于长度为奇数的串,我们一定可以找到 f(k)=0,即将 ak 从 S 删除即可满足题意。
如果长度为偶数的串,可以先删除 an 使得它的长度变为奇数且 s(1,n−1) 不会发生变化,然后再按照奇数的删除方法删除。
综上,最大的删除字符个数不会超过2个。
拓展到任意 S 的子串 T 方法很简单,只需要求出 s(T) 即可,所以可以通过维护函数 s 的前缀和完成。
于是对于长度为 n 的字符串 S:
时间复杂度 O(N)。
下面给出 D1(easy version) 的代码:
点击显/隐代码
对于 D2 (hard version) 只需要求出每次奇数长度时删除的位置 k 即可。
我们知道计算函数零点,可以通过二分完成,所以直接二分零点即可。
时间复杂度 O(NlogN)。
点击显/隐代码
E - Rescue Niwen!
题意
给出一个长度为 N 的字符串 S,以 S 的子串:S1,S1,2,S1,3,…,S1,n,S2,S2,3,S2,4,…,S2,n,…,Sn−1,Sn−1,n,Sn,构成的字符串序列中,最长单调递增子序列长度为多少?(字符串排序方法按照字典序排序,单调在这里是指严格单调)
数据范围:1⩽N⩽5000。
思路
最开始想先预处理出来所有子串的字典序大小,用字典序大小代替原来的字符串序列,然后直接通过 lower_bound
求最长上升子序列,这样复杂度是 O(N2logN2) 理论上过不了,最终优化到了 3s,实在写不进 2s 了,最后放弃了这种写法,后来看题解发现这道题有很好的性质。
如果字符串 Sl1,r1 在最长上升子序列中,则 Sl1,n 也一定在其中。
所以如果 Sl2,r2 考虑从前一个转移,就只用考虑从 Sl1,n 进行转移,保证 Sl2,r2>Sl1,n。
这里可以通过求每个后缀 Sl1,n,Sl2,n 的 lcp(最长公共前缀)完成,设它们的lcp长度为 k。
设 f(i) 表示左端点从 1∼i 的最长上升子序列长度,用贪心的思路进行转移。
则 f(i)=max(n−i+1,f(j)+n−i+1−k),j<i,且保证 Si+k>Sj+k。
至于两个后缀的lcp可以直接暴力dp得到,这里复杂度也只是 O(N2)。
时间复杂度 O(N2)。
正确代码:
点击显/隐代码
尝试后的错误代码,使用SAM求字典序,然后lower_bound
求解最长上升子序列:
点击显/隐代码