Codeforces Round #644 1360H. Binary Median(思维、构造)

题目描述:
time limit per test
2 seconds
memory limit per test
256 megabytes

Consider all binary strings of length m (1≤m≤60). A binary string is a string that consists of the characters 0 and 1 only. For example, 0110 is a binary string, and 012aba is not. Obviously, there are exactly 2m such strings in total.

The string s is lexicographically smaller than the string t (both have the same length m) if in the first position i from the left in which they differ, we have s[i]

We remove from this set n (1≤n≤min(2m−1,100)) distinct binary strings a1,a2,…,an, each of length m. Thus, the set will have k=2m−n strings. Sort all strings of the resulting set in lexicographical ascending order (as in the dictionary).

We number all the strings after sorting from 0 to k−1. Print the string whose index is ⌊(k−1)/2⌋ (such an element is called median), where ⌊x⌋ is the rounding of the number down to the nearest integer.

For example, if n=3, m=3 and a=[010, 111, 001], then after removing the strings ai and sorting, the result will take the form: [000, 011, 100, 101, 110]. Thus, the desired median is 100.

Input
The first line contains an integer t (1≤t≤1000) — the number of test cases. Then, t test cases follow.

The first line of each test case contains integers n (1≤n≤min(2m−1,100)) and m (1≤m≤60), where n is the number of strings to remove, and m is the length of binary strings. The next n lines contain a1,a2,…,an — distinct binary strings of length m.

The total length of all given binary strings in all test cases in one test does not exceed 105.

Output
Print t answers to the test cases. For each test case, print a string of length m — the median of the sorted sequence of remaining strings in the corresponding test case.

Example
input

5
3 3
010
001
111
4 3
000
111
100
011
1 1
1
1 1
0
3 2
00
01
10

output

100
010
0
1
11

Note
The first test case is explained in the statement.

In the second test case, the result after removing strings and sorting is [001, 010, 101, 110]. Therefore, the desired median is 010.

算法标签:思维、构造

题目大意:给定长度为m的所有二进制串(共2^m个),根据字典序从小到大排列后,删去其中n个串(a1,a2…,an),求删去后的中位串(第⌊(k−1)/2⌋个)

首先观察m<=60,可以直接将二进制串转化为十进制数,把问题转化到一根数轴上,不需要再从字符串角度考虑。

设最终答案为med,那么要求出答案只需要知道给出的n个数中,有几个在med右侧(>med),有几个在med左侧(

此处有两个思维上的难点:

  1. 当我们依次将ai与med比较时,med是在动态变化的(意味着当前关于ai和med大小的判断可能有误)
  2. 删去一个数后,数列长度发生改变,其余点的中位数求起来也比较麻烦。

所以为了解决以上两个问题,可以简单构造一下。

如图,一开始,我们假设给出的n个点都在med左侧,因为是连续的,所以这个数列的性质非常清晰。设数列左端l=n,右端r=2^m-1,那么实际数列长度为(r-l+1),中位数就是(l+r)/2

Codeforces Round #644 1360H. Binary Median(思维、构造)_第1张图片
但是当然ai也可能在med右侧,此时如何调整呢。之前说到,我们只关注这个ai在med左侧还是右侧,而不关注它具体是多少。所以,只要把任意两个med左侧和右侧的数互换一下就可以了。为了维护数列的连续性,我们可以交换n-1和2^m-1这两个数。
Codeforces Round #644 1360H. Binary Median(思维、构造)_第2张图片
把上面两张图对比着看一下,很显然,我们只是把蓝色的一段整体左移了一个数而已,那么med也只需要减1就可以完成变换了。

最后一个问题是,med仍然是动态变化的,如何在判断每个ai“在med左侧还是右侧”的时候都能确保判断是正确的呢。

只要先将ai从大到小排序即可。(具体说是因为这种构造方式维护了med的单调递减,那么排序后如果ai>=med,ai一定大于等于最终的中位数,如果ai

所以这种构造法最关键的地方是维护了区间长度和连续性,对于一个固定长度并连续的区间,处理起来就非常方便。

相反,如果完全按照题意进行模拟,一些边界情况的处理可能会变得非常烧脑,且不易调试和理解。(当然,这对思维能力和码力足够的大佬来说可能并不是什么问题…)

#include
using namespace std;
typedef long long ll;

const int N=100+5;

char s[N],ans[N];

ll l,r,jc[N],A[N];

bool cmp(ll a,ll b){ return a>b; }
int main()
{
    int T,n,m;
    jc[0]=1;
    for (int i=1;i<=60;i++) jc[i]=jc[i-1]*2;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        l=0,r=jc[m]-1;
        for (int i=1;i<=n;i++)
        {
            scanf("%s",s);
            ll tmp=0;
            for (int j=0;j<m;j++)
            {
                tmp=tmp*2+s[j]-'0';
            }
            A[i]=tmp;
        }
        sort(A+1,A+1+n,cmp);
        ll cur=(n+r)/2;
        for (int i=1;i<=n;i++)
        {
            if (A[i]>=cur)
                cur--;
        }
        ans[m]='\0';
        for (int i=m-1;i>=0;i--)
        {
            ans[i]=cur%2+'0';
            cur/=2;
        }
        printf("%s\n",ans);
    }
    return 0;
}

你可能感兴趣的:(Codeforces Round #644 1360H. Binary Median(思维、构造))