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\)位格雷码由两个\(1\)位二进制串组成,顺序为:\(0,1\)

  2. \(n+1\)位格雷码的前\(2^n\)个二进制串,可以由依此算法生成的\(n\)位格雷码(总共\(2^n\)\(n\)位二进制串)按顺序排列,再在每个串前加一个前缀\(0\)构成。

  3. \(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. 已知\(1\)位格雷码为 \(0,1\)

  2. 前两个格雷码为 \(00,01\)。后两个格雷码为\(11,10\)。合并得到 \(00,01,11,10\),编号依次为 \(0 ~ 3\)

同理,\(3\) 位格雷码可以这样推出:

  1. 已知\(2\)位格雷码为:\(00,01,11,10\)

  2. 前四个格雷码为:\(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;
}
posted @ 2020-07-27 19:15  大秦帝国  阅读(520)  评论(0编辑  收藏  举报