一般,人们习惯将全部 \(n\) 位二进制串按照字典序排列,例如全部 \(2\) 位二进制串按字典序从小到大排列为:\(00,01,11,10\)。ios
格雷码(\(Gray Code\))是一种特殊的 \(n\) 位二进制串排列法,它要求相邻的两个二进制串间刚好有一位不一样,特别地,第一个串与最后一个串也算做相邻。git
全部 \(2\) 位二进制串按格雷码排列的一个例子为:\(00\),\(01\),\(11\),\(10\)。算法
\(n\) 位格雷码不止一种,下面给出其中一种格雷码的生成算法:spa
综上,\(n + 1\) 位格雷码,由 \(n\) 位格雷码的 \(2^n\)个二进制串按顺序排列再加前缀 \(0\),和按逆序排列再加前缀 \(1\) 构成,共 \(2^{n+1}\) 个二进制串。另外,对于 \(n\) 位格雷码中的 \(2^n\)个 二进制串,咱们按上述算法获得的排列顺序将它们从 \(0 \sim 2^n - 1\) 编号。code
按该算法,\(2\)位格雷码能够这样推出:get
同理,\(3\) 位格雷码能够这样推出:string
如今给出 \(n\),\(k\),请你求出按上述算法生成的 \(n\) 位格雷码中的 \(k\) 号二进制串。it
仅一行两个整数 \(n\),\(k\),意义见题目描述。io
仅一行一个 \(n\) 位二进制串表示答案。class
输入 #1
2 3
输出 #1
10
输入 #2
3 5
输出 #2
111
输入 #3
44 1145141919810
输出 #3
00011000111111010000001001001000000001100011
【样例 \(1\) 解释】
\(2\) 位格雷码为:\(00\),\(01\),\(11\),\(10\),编号从 \(0\sim3\),所以 \(3\) 号串是 \(10\)。
【样例 \(2\) 解释】
\(3\) 位格雷码为:\(000\),\(001\),\(011\),\(010\),\(110\),\(111\),\(101\),\(100\),编号从 \(0\sim7\),所以 \(5\) 号串是 \(111\)。
【数据范围】
对于 \(50\%\) 的数据:\(n \leq 10\)
对于 \(80\%\) 的数据:\(k \leq 5 \times 10^6\)
对于 \(95\%\) 的数据:\(k \leq 2^{63} - 1\)
对于 \(100\%\) 的数据:\(1 \leq n \leq 64\), \(0 \leq k \lt 2^n\)
这个题正解听说是位运算,可是彷佛也不用这么麻烦。然而我考场上并无开\(unsigned\) \(long\) \(long\)因此我就没了。
考虑按照题意模拟。按照题意,一个\(n\)位的格雷码是由一个前缀\(0\)或\(1\)加上一个长度为\(n-1\)为的格雷码构成的,因此咱们能够考虑相似康托展开的方法。
咱们不妨先处理出全部\(2\)的幂。对于咱们知道这个长度为\(n\)的格雷码在当前全部长度为\(n\)的格雷码中应该正序排第\(k\)位,则若是\(n\lt 2^{n-1}\)(只有小因而由于编号是从\(0\)开始存的)则当前这一位还放\(0\),转移到\(dfs(n-1,k)\);反之放下一个\(1\),考虑怎么处理逆序。
因为咱们已经放下了一个\(1\),因此咱们已经整个过滤掉了\(2^{n-1}\)个比它排名靠前的格雷码(由于这些格雷码第一位应该是\(0\)),因此咱们最后处理编号的范围是\(2^{n-1}\),因此咱们首先要把\(k\)减掉\(2^{n-1}\)。手玩一下能够知道,编号从\(0\)开始存这个事情很是麻烦,因此咱们须要把这个数整个向右面移一位,也就是加上\(1\)。
再考虑要求倒序排列。这个很简单,由于这\(2^{n-1}\)个格雷码的编号是\(0\sim2^{n-1}-1\),因此容易知道咱们只须要用\(2^{n-1}-(k-2^{n-1}+1)\)便可。(其实手玩一下或者打表找规律也行。)
上代码。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cctype> #include<cmath> #define int long long #define rep(i,a,n) for(register int i=a;i<=n;++i) #define dep(i,n,a) for(register int i=n;i>=a;--i) using namespace std; int n; unsigned long long k; unsigned long long num[64]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } void write(int x) { if(x<0)putchar('-'),x=-x; if(x==0)return; write(x/10); putchar(x%10+'0'); } void dfs(int step,int k) { if(step==0)return; if(k<num[step-1]) { putchar('0'); dfs(step-1,k); } else { putchar('1'); dfs(step-1,num[step-1]-(k-num[step-1]+1)); } } signed main() { n=read(),k=read(); num[0]=1; rep(i,1,n-1)num[i]=num[i-1]*2; dfs(n,k); return 0; }
必定注意编号必须从\(0\)存,不然\(unsigned\) \(long\) \(long\)存不下。