CF1360H Binary Median
原题链接 https://www.luogu.com.cn/problem/CF1360H
题目大意
题解
① 最常见最暴力的做法:
将读入的二进制转化为十进制,然后依次删除,删除过程中维护中位数;
虽然思路简单,但是考虑到实际维护的过程中十分复杂,对代码能力要求较高;
② 稍微巧妙一点的做法:
其实我们并不需要维护每个过程中的中位数是几,我们关心的只是全部删除后的中位数是几;
而且题目中给出了数据的原始个数(2m)和删除个数(n),也就是说,我们能知道删除完后的这 2m - n 的中位数是第几个数(设是第 k 个数,根据规律可得 k = ( 2m - n + 1 ) / 2),那么删除完后我们就输出这个位置的数就好了;
这就有了另一个思路:我们只需要在删除过程中维护哪个数变成了第 k 个数 。
拿样例模拟一下:
n = 3,m = 3
删除的数为:(010)2 = 2,(001)2 = 1 ,(111)2 = 7
删除前的所有数:
通过计算可以知道 k = ( 23 - 3 + 1 ) / 2 = 3,那么我们就确定了删完 3 个数之后的数据中第 3 个数是中位数;
然后我们就死盯着第 3 个数不放,看哪个数在删除之后移到了第 3 个数的位置(红色的数字代表最后的中位数上的数);
由于数据是从 0 开始的,所以第 k 个数的大小为 k-1 (令 mid = k-1)。
删除 2:
删除 1:
删除 7:
过程中我们可以发现一个规律:
对于我们所要维护的中位数 mid 来说,如果删除的数比 mid 大,那么 mid 的值是不变的(参考上图删除 7 的过程);如果删除的数小于等于 mid,那么 mid 的值就变成了右边的那个数(以下称为“ 右移一次 ”),注意要跳过已经被删除了的数;
这么看来,我们可以先将所需删除的数从大到小排序,对于其中的 cnt 个比 mid 大的数,删除了并不影响 mid 的值;对于其中的 n - cnt 个小于等于 mid 的数,每删除一个 mid 就要右移一次,总共需要右移 n - cnt 次;
其实我们也可以只存比 mid 大的那些数,对于那些比 mid 小的数,我们只关心它有多少个;同时这样的话,我们也不必将所删除的数从大到小排序了,因为二分查找也可以从小到大排序啊qwq 。
Code:
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,n,m,cnt; long long len,mid; long long delet[1500]; bool read() //读入单个数字 { char ch=getchar(); while(ch!='0'&&ch!='1') ch=getchar(); return ch^48; } int check(long long x) //查找x是否在delet数组里 { return delet[lower_bound(delet+1,delet+1+cnt,x)-delet]==x; //lower_bound偷懒qwq } void work() { for(long long i=mid+1;i<=len;i++) { if(!check(i)) //判断i是否被删除 { mid=i; //没有被删除就来当mid return ; } } } int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&n,&m); len=1ll<<m; //len是初始数的个数 mid=(len-n+1)/2-1; //删除一些数之后的中位数的位置上的数现在是几 memset(delet,0,sizeof(delet)); cnt=0; //记录删除的数中有多少个数比mid大 for(int i=1;i<=n;i++) { long long a=len/2; long long num=0; //num是转化为十进制之后的数 for(int j=1;j<=m;j++) //二进制转化为十进制 { int x=read(); if(x) num+=a; a/=2; } if(num>mid) delet[++cnt]=num; //只需记录所有比mid大的数 } sort(delet+1,delet+1+cnt); //从小到大排序,便于二分查找 for(int i=1;i<=n-cnt;i++) //总共有n-cnt个数小于等于mid,就要右移n-cnt次 work(); len/=2; while(len) //十进制转化为二进制 { if(mid>=len) { printf("1"); mid-=len; } else printf("0"); len/=2; } printf("\n"); } return 0; }
特别鸣谢 一扶苏一 帮我 debug;