# 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);
}
posted @ 2020-07-30 10:13  熹圜  阅读(223)  评论(0)    收藏  举报