上海大学程序设计联赛 F_1+2=3?(位运算+对斐波拉契神奇的发现)

上海大学程序设计联赛 F_1+2=3?(位运算)

最近遇到挺多位运算的题目,感觉有些题还是要做下总结,来记住位运算神奇的特性

题目:

小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式x2x=3xx⨁2x=3x,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。
(表示按位异或运算)

输出描述:

第一行是一个正整数T(T100)T(T≤100),表示查询次数。
接着有T行,每行有一个正整数N(N1012)N(N≤1012),表示小Y的查询。

输出描述:

对于每一个查询N,输出第N个满足题中等式的正整数,并换行。

样例1:

输入

4
1
2
3
10

输出

1
2
4
18

题解:

首先要观察一下题目的公式x2x=3xx⨁2x=3x,我们可以作出这样的翻译:
一个二进制数xx加上这个数左移一位2x2x等于这两个数的异或和
举个例子:
x=9x=9    转化为二进制是0000100100001001
2x=182x=18转化为二进制是0001001000010010
3x=273x=27转化为二进制是0001101100011011
我们很容易就可以发现等式成立只要xx的二进制表示里面的11是不连续的!
然后我们先想不关注题目想一个简单的问题:
112n2n里面有多少个满足这样数?
我们先简单列一下表观察一下
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=01100→看作01000+01000=10000
而第三行之所以能这么看是因为01100是不满足条件的需要进一位,也就是说,要把00100换成01000。这也就是为什么有一个+11+1−1
因此,现在我们可以通过递推式知道了112n2n里面有多少个满足这样数。
回到题目来看,我们现在知道某个满足条件数K是第N个满足条件的数,然后我们知道每f[i]名在二进制第i+1位就有一个1,ans就加上2i2i,然后我们通过不断减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;
    }
}

转载于:https://www.cnblogs.com/na7-TRZNDP-Z/p/9782459.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值