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]<t[i]. This is exactly the way strings are compared in dictionaries and in most modern programming languages when comparing them in a standard way. For example, the string 01011 is lexicographically smaller than the string 01100, because the first two characters are the same, and the third character in the first string is less than that in the second.

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左侧(<med)即可。

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

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0yhb07F8-1590493347267)(图1.png)]
但是当然ai也可能在med右侧,此时如何调整呢。之前说到,我们只关注这个ai在med左侧还是右侧,而不关注它具体是多少。所以,只要把任意两个med左侧和右侧的数互换一下就可以了。为了维护数列的连续性,我们可以交换n-1和2^m-1这两个数。
在这里插入图片描述
把上面两张图对比着看一下,很显然,我们只是把蓝色的一段整体左移了一个数而已,那么med也只需要减1就可以完成变换了。

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

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

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

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

#include<bits/stdc++.h>
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;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值