题目:格雷码
网址:https://www.luogu.com.cn/problem/P5657
通常,人们习惯将所有(n)位二进制串按照字典序排列,例如所有(2)位二进制串按字典序从小到大排列为:(00,01,10,11)。
格雷码(Gray Code)是一种特殊的(n)位二进制串排列法,它要求相邻的两个二进制串间恰好有一位不同,特别地,第一个串与最后一个串也算作相邻。
所有(2)位二进制串按格雷码排列的一个例子为:(00,01,11,10)。
(n)位格雷码不止一种,下面给出其中一种格雷码的生成算法:
-
(1)位格雷码由两个(1)位二进制串组成,顺序为:(0,1)。
-
(n+1)位格雷码的前(2^n)个二进制串,可以由依此算法生成的(n)位格雷码(总共(2^n)个(n)位二进制串)按顺序排列,再在每个串前加一个前缀(0)构成。
-
(n+1) 位格雷码的后(2^n)个二进制串,可以由依此算法生成的(n)位格雷码(总共(2^n)个(n)位二进制串)按逆序排列,再在每个串前加一个前缀(1)构成。
综上,(n+1)位格雷码,由(n)位格雷码的(2^n)个二进制串按顺序排列再加前缀(0),和按逆序排列再加前缀(1)构成,共(2^{n+1})个二进制串。另外,对于(n)位格雷码中的(2^n)个二进制串,我们按上述算法得到的排列顺序将它们从(0∼2^n−1)编号。
按该算法,(2)位格雷码可以这样推出:
-
已知(1)位格雷码为 (0,1)。
-
前两个格雷码为 (00,01)。后两个格雷码为(11,10)。合并得到 (00,01,11,10),编号依次为 (0 ~ 3)。
同理,(3) 位格雷码可以这样推出:
-
已知(2)位格雷码为:(00,01,11,10)。
-
前四个格雷码为:(000,001,011,010)。后四个格雷码为:(110,111,101,100)。合并得到:(000,001,011,010,110,111,101,100),编号依次为 (0 ~ 7)。
现在给出 (n,k),请你求出按上述算法生成的(n)位格雷码中的(k)号二进制串。
输入格式
仅一行两个整数 (n,k),意义见题目描述。
输出格式
仅一行一个(n)位二进制串表示答案。
输入输出样例
输入
2 3
输出
10
输入 #2
3 5
输出 #2
111
输入 #3
44 1145141919810
输出 #3
00011000111111010000001001001000000001100011
说明/提示
【样例(1)解释】
(2) 位格雷码为:(00,01,11,10),编号从(0∼3),因此(3)号串是(10)。
【样例(2)解释】
(3)位格雷码为:(000,001,011,010,110,111,101,100),编号从(0∼7),因此(5)号串是(111)。
【数据范围】
对于(50%)的数据:(0≤n≤10)
对于(80%)的数据:(k≤5×10^6)
对于(95%)的数据:(k≤2^{63})
对于(100%)的数据:(1≤n≤64,0≤k<2^n)
如果模拟,那么只能过(50)分。
首先,确定第(k)个数的位置在哪里。若(k<2^n),该数在序列前半部分;反之,则在右半部分。
该数的第(n)位就是如此确定下来的:若在前半部分,第(n)位数上为(0);若为后半部分,第(n)位数上为(1)。
我们继续。
若该数位于前半部分,那么(n-1)位即可以按上述规律确定;如果不幸在后面了,(n-1)位规律是相反的。
分治!
换句话说,确定第(i+2)位数之后,该数的位置仅影响的是第(i+1)位的确定,但跟第(i)位毫无关联。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ull unsigned long long
using namespace std;
const int maxn = 64 + 5;
ull n, k, p[maxn];
int main()
{
cin >> n >> k;
p[0] = 1;
for(int i = 1; i <= n; ++ i) p[i] = p[i - 1] << 1ll;
int cur = 0;
for(int i = n; i > 0; -- i)
{
if(!cur)
{
if(k < p[i - 1])
{
putchar('0');
cur = 0;
}
else
{
putchar('1');
cur = 1;
}
}
else
{
if(k < p[i - 1])
{
putchar('1');
cur = 0;
}
else
{
putchar('0');
cur = 1;
}
}
k %= p[i - 1];
}
return 0;
}