Xor Sum AtCoder - 2272 dp 转移方程

参考博客
题目链接
题解 建议翻译成英文看

You are given a positive integer N. Find the number of the pairs of integers u and v (0≦u,v≦N) such that there exist two non-negative integers a and b satisfying a xor b=u and a+b=v. Here, xor denotes the bitwise exclusive OR. Since it can be extremely large, compute the answer modulo 109+7.

Constraints
1≦N≦1018
Input
The input is given from Standard Input in the following format:

N
Output
Print the number of the possible pairs of integers u and v, modulo 109+7.

Sample Input 1
3
Sample Output 1
5
The five possible pairs of u and v are:

u=0,v=0 (Let a=0,b=0, then 0 xor 0=0, 0+0=0.)

u=0,v=2 (Let a=1,b=1, then 1 xor 1=0, 1+1=2.)

u=1,v=1 (Let a=1,b=0, then 1 xor 0=1, 1+0=1.)

u=2,v=2 (Let a=2,b=0, then 2 xor 0=2, 2+0=2.)

u=3,v=3 (Let a=3,b=0, then 3 xor 0=3, 3+0=3.)

Sample Input 2
1422
Sample Output 2
52277
Sample Input 3
1000000000000000000
Sample Output 3
787014179

题意:求有多少个数字对< u,v >满足 0≤u,v≤N 并且存在两个整数 a,b 使得a+b=u,a xor b=v
分析:我们发现只要 a,b 不超过 N,那么a xor b 也不会超过N,所以关键就是要保证 a+b 不超过 N,那么问题就转换成找到有多少组 a,b 使得 a+b≤N

首先这个题目要有上一步分析中的转换,接下来就是如何推递推方程,首先为了避免重复我们令a>=b
接着有两个初始解 F[0] F[1]
他们两个的a,b 分别是{0,0}和{(0,0),(1,0)}
接下来要接着往下递推就要考虑a+b<=n时二进制是如何由小到大转换过来的,首先从小到大转换时,假如直接转换二进制的的最右端的位数 来实现从小到大的转换这样递推式的右端项的个数会随着数的位数发生变化,因此形不成递推式,于是我们采用从要递推的数n的二分之一处寻找变化,我们计算只考虑{a,b}为为{1,0}的情况,我们知道整数在除2时,会发生向下取整的情况,因此一个数n 在由小数向大数递推的过程中总共有三种情况,分别是(n/2 + n/2 ),(n/2+1 + n/2),(n/2+1, n/2+1)这三种情况有时候加起来会小于等于n但是绝不能让他们出现大于n的情况,这就需要在递推的过程中一一对应,例如递推F[2]时,F[2] = F[1]+F[0]+F[0],
即 (0,0) (1,0) 对应F[1] 进行(n*2,n*2)的操作得到(0,0) (2,0),(0,0)对应第一个F[0]进行(n*2+1,n*2)的操作 得到 (1,0) ,(0,0)对应第二个F[0]进行(n*2+1,n*2+1)的操作得到(1,1), 最后F[2] = 4 ,即(0,0)(1,0)(1,1)(2,0)这四个(a,b)数对, 递推F[3]时 同样有

F[3] = F[1]+F[1]+F[0]
第一项F[1]进行(n*2,n*2)操作
 (00)  (00)
 (10)   (20)
第二项F[1]进行(n*2+1,n*2)操作
 (00)   (10)
 (10)    (30)
 第三项F[0]进行(n*2+1,n*2+1)操作
 (00)   (11

其实在本质上看的二进制操作也比较好理解,要得到小于等于n的数,第一种操作是n/2即先将n>>1,然后n<<1,这样最后得到的数肯定不会超过n,第二种操作是((N-1)/2)*2+((N-1)/2),第三种操作是((N-2)/2)*2+1 + ((N-2)/2)*2+1 ,三种操作目的很明显,完成递推且数对数对(a,b)进行的操作不能使a+b出现大于n的情况,
所以下面的状态转移方程就好理解了
这里写图片描述

code:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const long long  mod = 1e9+7;
map<long long ,long long>dp;
#define bug cout<<88888888<<endl;
#define debug(x) cout << #x" = " << x << endl;
long long Dp(long long x)
{
    if(dp[x])return dp[x];
    else
    {
        return dp[x] = ((Dp(x/2)+Dp((x-1)/2)+Dp((x-2)/2)) )%mod;
    }
}
int main()
{
    dp[0] = 1;
    dp[1] = 2;
    long long n;
    cin>>n;
    cout<<Dp(n);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值