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\)位格雷碼可以這樣推出:
-
已知\(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;
}