首页 > 代码库 > 【题解】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