NOIP2019 格雷码 [提高组]
题目:格雷码
网址: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;
}