# NOIP2019_Day1T1_格雷码
题意:
\(n\) 位格雷码指有 \(2^{n}\) 个长度为 \(n\) 的01字符串的排列,且相邻两个字符串只有一位不同,例如:
1位格雷码:0,1
2位格雷码:00,01,11,10
3位格雷码:000,001,011,010,110,111,101,100
显然,\(i+1\) 位格雷码的个数是 \(i\) 位格雷码个数的2倍
现有一种生成格雷码的算法:
规定一位格雷码为 1,0
\(n\) 位格雷码的前 \(2^{n-1}\) 个可由 \(n-1\) 位格雷码顺序排列,并在每个串的前面补上一个0得到
\(n\) 位格雷码的后 \(2^{n-1}\) 个可由 \(n-1\) 位格雷码逆序排列,并在每个串的前面补上一个1得到
那么问题来了,给定 \(n, k\quad n\ <=\ 64,\ k\ <=\ 2^{64}\) ,求 \(n\) 位格雷码的第 \(k\) 个是多少?
解:
找规律,看看 \(k\) 和答案有什么关系
答案的 0位 1位 2位 ...
k = 0000 0 0 0
0001 1 0 0
0010 1 1 0
0011 0 1 0
0100 0 1 1
0101 1 1 1
0110 1 0 1
0111 0 0 1
1000 0 0 1
1001 1 0 1
1010 1 1 1
1011 0 1 1
1100 0 1 0
1101 1 1 0
1110 1 0 0
1111 0 0 0
emm...貌似有点什么规律,但又说不清...
但是当你把 \(k\) 的0,1位与答案的0位比较————你会惊人的发现答案的0位恰巧等于 \(k\) 的0,1位异或结果!
同理,把 \(k\) 的1, 2位与答案的1位比较发现答案的1位恰等于 \(k\) 的1,2位异或结果
那么我们有了一个大胆的想法:把 \(k\) 整体向右移一位再与 \(k\) 本身异或,得出的就是结果!
至于是 \(n\) 位嘛,顺次输出结果的从 \(n-1\) 到0位就好了
代码:
所以就有了这令人惊叹的代码
*k一定要开ull,因为普通ll最大只能记录 \(2^{63}-1\) ,但是k要求最大到 \(2^{64}\)
#include <bits/stdc++.h>
using namespace std;
int n; unsigned long long k;
int main(){
cin >> n >> k;
k ^= k >> 1;
while(~--n)
cout << (k >> n & 1);
}

浙公网安备 33010602011771号