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

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

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

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

数据范围:$n≤25,k≤10^{16}$。

 

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

看到这一题:打表啊!

于是打了个表,发现:

若$k≤2^{\lceil \frac{n}{2} \rceil}-2$,则直接输出$k$就可以了,证明显然。

若$k>2^{\lceil \frac{n}{2} \rceil}-2$

 

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

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

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

我们基于这一个特征,先确定这个二进制数的前$\frac{n}{2}$位。

后面的$\frac{n}{2}$位直接暴力枚举然后再随便判断一下就好了。

 

n为奇数的情况相似

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

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

 

时间复杂度:$O(2^{n/2})$,空间复杂度:$O(2^{n/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 @ 2019-03-12 20:34  AlphaInf  阅读(164)  评论(0编辑  收藏  举报