首页 > 代码库 > 【题解】Recover the String Codeforces 708B
【题解】Recover the String Codeforces 708B
题解讲的不是很详细,有些定理未加证明地直接给出,需要读者自己思考,如有不懂欢迎在讨论区提问
由00和11的个数可以确定0和1的个数
假设00的个数是b,必须满足a*(a-1)/2=b,且a是整数
这时候,a便是0的个数,求1的个数同理
同时,01和10的个数的和 一定是 0的个数*1的个数
然后构造一个00000000001111111111这样的串,是不存在10这样的子序列的
我们称最右边的一坨1叫“没有交换的1”,然后每次选择这里面最左边的一个1和第x(x待确定)个0交换
注意到,和第x个0交换之后,产生的10的个数是数字1跨过的0的个数
真的是这样么?
对于这样的一个串0000011111,我们假设第一步是第一个1和第二个0交换,跨过了4个0
变成0100001111,这时候,我们执行第二步
发现“没有交换的1”中,最左边的1和第2,3,4,5个0交换的时候,满足上面的定律
但是和第一个0交换,就不满足了,因为跨过了一个1
于是我们想让交换不跨过1,即满足,每次交换到第x个0,这个x是非降的
于是每次交换就尽量和左边的0交换,并累计产生的10的数量,根据上面的分析,很容易确定到底和第几个0交换
问题就变成了“查询第x个0的位置”
平衡树
以上是我的智障做法,然后zrf大神教我做人:
前提是满足00,11,10,01的个数限制,就是上面讨论到的那套基本法
从左到右依次放0或者1,保存还有多少个0没有放
假如还有x个0没有放,那么我们放一个1,产生的10的个数就是x
在不超过10的个数的限制下尽量把1放在前面,贪心
线性
等等!还没完!在我WA了几遍之后,发现要特殊处理a00 == 0和a11 == 0的情况
注意到a00 == 0的时候,最终的答案可以有一个0,也可以没有0
这时候到底有没有0取决于a10和a01的数量
即,当a10 == 0 && a01 == 0 && a00 == 0的时候,没有0
当a10 != 0 && a01 != 0 && a00 == 0的时候,有一个0
对于数字1也是一样,讨论一下就好了
为什么我如此智障呢?我被数据范围坑了
按照数据范围,数字最多有90000个,于是我就想O(nlogn)
线性做法一开始想到了,后来觉得Div1B不可能这么naive。。。就弃了
下面贴代码,没有注释,不过应该比较好懂吧。。。不懂的欢迎在讨论区提问
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstdio> 6 #include <cstdlib> 7 8 using namespace std; 9 10 int a00, a01, a10, a11; 11 int a0, a1, n; 12 13 void quit() { 14 cout << "Impossible" << endl; exit(0); 15 } 16 17 int main() { 18 cin >> a00 >> a01 >> a10 >> a11; 19 a0 = sqrt(a00<<1)+1; a1 = sqrt(a11<<1)+1; 20 if( a01+a10+a11 == 0 ) a1 = 0; 21 if( a01+a10+a00 == 0 ) a0 = 0; 22 if( a0*(a0-1)/2 != a00 || a1*(a1-1)/2 != a11 ) quit(); 23 if( a01+a10 != a0*a1 ) quit(); 24 n = a0+a1; 25 for( int i = 0; i < n; ++i ) { 26 if( a10 >= a0 ) cout << 1, a10 -= a0; 27 else cout << 0, --a0; 28 } 29 if( !n ) cout << 0; 30 return 0; 31 }
【题解】Recover the String Codeforces 708B