题目
解说
去年NOIP省赛题,当时就学了1个月OI所以第一题就惨遭爆零,如今再回来刷一遍过了……
总体思路就是把格雷码当成十进制的数字进行处理,最后再转化回二进制。转化后的样子如下:
(0 1)
(0 1 3 2)
(0 1 3 2 6 7 5 4)
(dots)
假设最终结果为(f(n,k))。
不难发现如果要求的数在该行的左半边:
即(k<2^{n-1})
那么(f(n,k)=f(n-1,k))
如果要求的数在该行的右半边:
即(k>=2^{n-1})
那么(f(n,k)=2^{n-1}+f(n,2^{n}-1-k))
这样的话我们就可以用递归来解决,最后的返回条件就是(n=1)时若(k=0)则返回(0),若(k=1)则返回(1)。
(以下代码中(ll)代表(unsigned long long))
ll dfs(int num,ll a){
if(num==1&&a==0) return 0;
if(num==1&&a==1) return 1;
if(a<=power(num-1)-1) return dfs(num-1,a);
return power(num-1)+dfs(num,power(num)-1-a);
}
但这时我们注意到一个问题:(num=64)时(2^{64})会炸(unsigned long long)……
那我们特判一下不就行了。
ll dfs(int num,ll a){
if(num==1&&a==0) return 0;
if(num==1&&a==1) return 1;
if(a<=power(num-1)-1) return dfs(num-1,a);
if(num<64) return power(num-1)+dfs(num,power(num)-1-a);
else return power(num-1)+dfs(num,power(63)-1+power(63)-a);
}
最后再把数字转化为二进制即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;//记得开unsigned long long
int n;
ll k;
bool ans[70];
ll power(int x){//快速幂
ll ans=1,a=2;
while(x){
if(x&1) ans*=a;
x>>=1;
a*=a;
}
return ans;
}
ll dfs(int num,ll a){//递归
if(num==1&&a==0) return 0;
if(num==1&&a==1) return 1;
if(a<=power(num-1)-1) return dfs(num-1,a);
if(num<64) return power(num-1)+dfs(num,power(num)-1-a);
else return power(num-1)+dfs(num,power(63)+9223372036854775807-a);
}
void change(ll x){//转化为二进制
int cnt=0;
while(x){
ans[cnt]=x%2;
x/=2;
cnt++;
}
}
int main(){
cin>>n>>k;
change(dfs(n,k));
for(int i=n-1;i>=0;i--) cout<<ans[i];
return 0;
}
幸甚至哉,歌以咏志。