【xsy1281】 珠串 打表+乱搞or数位dp

题目大意:你要找出一个有k个的本质不同的n位二进制数的集合,使得集合中最大的数最小,请输出这个数

本质不同定义:对于一个数krev(k) krev( k)k本质相同。其中 k表示对k的每一位二进制翻转,rev(k)表示对k左右翻转。

举个例子:对于数0001,它与1000,1110,0111本质相同。

数据范围:n25,k1016

 

此题貌似正解是数位dp,然而我比较菜。

看到这一题:打表啊!

于是打了个表,发现:

k2n22,则直接输出k就可以了,证明显然。

k>2n22

 

先考虑n为偶数的情况,我们打一个表,打出所有满足rev(x)<x( x)<xrev( x)<x的数,大概长这样

我们发现:以中间的分界线为界,当左侧构成的数去掉前导零后构成x,那么以x开头的不符合要求的二进制数就有2x个。

(感兴趣的同学可以证明一下,我懒得证了23333)

我们基于这一个特征,先确定这个二进制数的前n2位。

后面的n2位直接暴力枚举然后再随便判断一下就好了。

 

n为奇数的情况相似

以中间为分界线,当右侧横线左侧的数为x时,以x为前缀的n位二进制数有x个。

和之前的搞法一样随便搞一搞就可以了。

 

时间复杂度:O(2n/2),空间复杂度:O(2n/2)

复制代码
 1 #include<bits/stdc++.h>
 2 #define L long long
 3 using namespace std;
 4 
 5 int rev[1<<25]={0};
 6 L n,k;
 7 
 8 void out(L k1){for(L i=0;i<n;i++) printf("%d",bool((1LL<<(n-i-1))&k1));}
 9 
10 void SolveEven(){
11     L n2=n/2,s=1<<n2,all=1LL<<n;
12     if(k<s){
13         out(k);
14         return;
15     }else k-=s;
16     for(int i=1;i<s;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(s>>1):0);
17     for(int i=1,j=2;i<s;i++,j+=2){
18         if(k>=s-j) k-=s-j;
19         else{
20             int p; for(p=0;p<s&&k>=0;p++){
21                 L k1=(1LL*i)<<n2|p;
22                 L k2=(1LL*rev[p])<<n2|rev[i];
23                 L k3=(1LL*rev[(s-1-p)&(s-1)])<<n2|rev[s-1-i];
24                 if(k2>=k1&&k3>=k1) 
25                 k--;
26             }
27             L k1=(1LL*i)<<n2|(p-1);
28             out(k1);
29             return;
30         }
31     }
32     cout<<-1<<endl;
33 }
34 void SolveOdd(){
35     L n2=n/2,s=1<<n2,all=1LL<<n;
36     L N2=(n+1)/2,S=1<<N2;
37     if(k<S-1){
38         out(k);
39         return;
40     }else k-=S-1;
41     for(int i=1;i<s;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(s>>1):0);
42     for(int i=2,j=2;i<s;i++,j++){
43         if(k>=s-j) k-=s-j;
44         else{
45             int p; for(p=0;p<s&&k>=0;p++){
46                 L k1=(1LL*i)<<n2|p;
47                 L k2=((i&1)<<n2)|((1LL*rev[p])<<N2)|rev[i>>1];
48                 L k3=(((i&1)==0)<<n2)|((1LL*rev[(s-p-1)&(s-1)])<<N2)|rev[s-1-(i>>1)];
49                 if(k2>=k1&&k3>=k1)
50                 k--;
51             }
52             L k1=(1LL*i)<<n2|(p-1);
53             out(k1);
54             return;
55         }
56     }
57     cout<<-1<<endl;
58 }
59 
60 int main(){
61     cin>>n>>k;
62     if(n&1) SolveOdd();
63     else SolveEven();
64 }
复制代码
posted @   AlphaInf  阅读(168)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示