上海大学程序设计联赛 F_1+2=3?(位运算+对斐波拉契神奇的发现)
上海大学程序设计联赛 F_1+2=3?(位运算)
最近遇到挺多位运算的题目,感觉有些题还是要做下总结,来记住位运算神奇的特性
题目:
小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。
(表示按位异或运算)
输出描述:
第一行是一个正整数,表示查询次数。
接着有T行,每行有一个正整数,表示小Y的查询。
输出描述:
对于每一个查询N,输出第N个满足题中等式的正整数,并换行。
样例1:
输入
4
1
2
3
10
输出
1
2
4
18
题解:
首先要观察一下题目的公式,我们可以作出这样的翻译:
一个二进制数加上这个数左移一位等于这两个数的异或和
举个例子:
转化为二进制是
转化为二进制是
转化为二进制是
我们很容易就可以发现等式成立只要的二进制表示里面的是不连续的!
然后我们先想不关注题目想一个简单的问题:
到里面有多少个满足这样数?
我们先简单列一下表观察一下
00001 f[0]=1
00010 f[1]=2
00100 f[2]=3
00101
01000 f[3]=5
01001
01010
10000 f[4]=8
我们可以发现 f[i]=f[i-1]+f[i-2] ,不过为什么呢?
实际上公式应该是 f[i]=f[i-1]+f[i-2]-1+1,我们可以看一下表:
在f[4]中我们可以分成两部分:
第一部分是:f[3]
第二部分则是:
01001
01010
10000
而第二部分也可以看成:
01000+00001=01001
01000+00010=01010
01000+00100=0110001000+01000=10000
而第三行之所以能这么看是因为01100是不满足条件的需要进一位,也就是说,要把00100换成01000。这也就是为什么有一个
因此,现在我们可以通过递推式知道了到里面有多少个满足这样数。
回到题目来看,我们现在知道某个满足条件数K是第N个满足条件的数,然后我们知道每f[i]名在二进制第i+1位就有一个1,ans就加上,然后我们通过不断减f[i](比N小才减),就可以反推出K。
代码:
#include<bits/stdc++.h>
using namespace std;
long long f[70];
long long g[70];
int main()
{
ios::sync_with_stdio(false);
int T,i,j,k;
long long n;
long long ans;
f[1]=1;f[2]=2;
g[1]=1;g[2]=2;
for(i=3;i<=60;i++)
{
g[i]=g[i-1]*2;
f[i]=f[i-1]+f[i-2];
}
cin>>T;
while(T--)
{
cin>>n;
ans=0;
for(i=60;i>=1;i--)
{
if(n>=f[i])
{
ans+=g[i];
n-=f[i];
}
}
cout<<ans<<endl;
}
}