D. Problem with Random Tests
D. Problem with Random Tests
You are given a string $s$ consisting of $n$ characters. Each character of $s$ is either $0$ or $1$.
A substring of $s$ is a contiguous subsequence of its characters.
You have to choose two substrings of $s$ (possibly intersecting, possibly the same, possibly non-intersecting — just any two substrings). After choosing them, you calculate the value of the chosen pair of substrings as follows:
let $s_1$ be the first substring, $s_2$ be the second chosen substring, and $f(s_i)$ be the integer such that $s_i$ is its binary representation (for example, if $s_i$ is $\text{11010}$, $f(s_i)=26$);
the value is the bitwise OR of $f(s_1)$ and $f(s_2)$.
Calculate the maximum possible value you can get, and print it in binary representation without leading zeroes.
Input
The first line contains one integer $n$ — the number of characters in $s$.
The second line contains $s$ itself, consisting of exactly $n$ characters $0$ and/or $1$.
All non-example tests in this problem are generated randomly: every character of $s$ is chosen independently of other characters; for each character, the probability of it being $1$ is exactly $\frac{1}{2}$.
This problem has exactly $40$ tests. Tests from $1$ to $3$ are the examples; tests from $4$ to $40$ are generated randomly. In tests from $4$ to $10$, $n=5$; in tests from $11$ to $20$, $n=1000$; in tests from $21$ to $40$, $n={10}^{6}$.
Hacks are forbidden in this problem.
Output
Print the maximum possible value you can get in binary representation without leading zeroes.
Examples
input
5 11010
output
11111
input
7 1110010
output
1111110
input
4 0000
output
0
解题思路
这题一直看错题,硬是把或看成异或,当时模拟样例的时候还一直出错,还以为是样例给错了,然后拿样例去问同学,结果人家题都没看就问我是不是按位或,当时心态直接炸了。
首先对于最优解,这两个子串中必然有一个子串是取整个字符串$s$。这是因为这个是按位或运算,位数越多得到的结果只可能变大而不会减小。考虑此时两个子串中不存在一个子串取整个$s$,那么我们选择其中一个子串,将其变成字符串$s$,那么得到的结果不会变得糟糕(因为位数增加)。
同时也可以发现另外一个子串前缀扩展到$s$的第一个字符$s[1]$结果也不会变差。
那么现在一个子串已经确定了(取整个字符串$s$),接下来考虑另外一个字符串。
对于字符串$s$我们从左往右找到第一个$1$出现的位置$p_1$,那么$s[1 \sim p_1]$内都是都是$0$,可以发现此时无论另外一个子串怎么取,都不可以将答案的$[1, p_1]$这个区间的任意一个位置置成$1$,因此答案最大是$[p_1, n]$这个区间内全为$1$。这时再从$p_1$这个位置开始往右找到第一个$0$的位置$p_2$,那么$s[p_1 \sim p_2 - 1]$内都是$1$。很明显我们要在另外一个子串的$p_2$位置取$1$,才能取到尽可能大的结果,而这个$1$就要在区间$[p_1, p_2 - 1]$内取(如果在$p_2$往后取的话,由于位数不足,因此另外一个子串的$p_2$位置必然是$0$)。那么我们就枚举区间$[p_1, p_2 - 1]$的每一个$1$,把这个$1$放到子串$p_2$这个位置,即这个子串就是$s[i \sim i + n - p_2] \, (p_1 \leq i \leq p_2 - 1)$。前面我们有说这个子串扩展到前缀结果不会变差,可以发现由于此时前缀是 0 0 ... 1 1 1 这个形式,结果也不会比$[p_1, n]$这个区间内全为$1$的更优,因此在这里是否扩展到前缀对结果没有影响,因此可以不用扩展到前缀。
然后是时间复杂度的问题,假设第一段连续的$1$的长度为$m$,那么时间复杂度就是$O(m \cdot n)$,但这题的数据是随机的,$0$和$1$出现的概率均为$\frac{1}{2}$,因此如果$m$要取到$20$,即第一段有$20$个连续的$1$,这个概率大概是${10}^{-6}$这个量级,即非常的低,因此我们可以认为$m$是一个比较小的常数,因此时间复杂度可以期望估计为$O(n)$。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() { 5 int n; 6 string s; 7 cin >> n >> s; 8 9 int p1 = s.find('1'); 10 if (p1 == -1) { 11 cout << '0'; 12 return 0; 13 } 14 15 int p2 = s.find('0', p1); 16 string ret = s; 17 for (int i = p1; i < p2; i++) { 18 string t = s; 19 for (int j = i + n - 1 - p2, k = n - 1; j >= i; j--, k--) { 20 t[k] |= s[j]; 21 } 22 ret = max(ret, t); 23 } 24 cout << ret.substr(ret.find('1')); 25 26 return 0; 27 }
参考资料
Educational Codeforces Round 137 Editorial:https://codeforces.com/blog/entry/108153
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16815463.html