题意:找有多少对uv满足存在两个整数ab使得a+b=u,a^b=v (^是异或)
分析:不难想到dp,但如何求状态转移方程呢?
这其实可以理解为数位dp(?)
首先有一点性质是a^b≤a+b,
这个很容易理解,因为当他们对应二进制位同为0时,a^b=a+b=0
当他们对应二进制位同为1时,a^b=0而a+b=2,也就是a+b>a^b
当他们对应二进制位分别为01时,a^b=a+b=1
显然a+b=(a&b)*2+a^b,这个式子后面也有点用,但现在主要就是这个a^b≤a+b这个式子
那么因为这个式子的存在,所以v≤u,也就是说我们可以令dp[i]表示u不超过i的uv数对的个数,当然也可以表示为a+b≤n,且a^b与a+b的数对的个数
首先这里就有个小问题就是,如果我们按照dp[i]从dp[i-1]推过来,显然先不说空间会不会超,因为有可能可以压,但时间必定会超
所以这里要用一点数位的思想
那么还是先把比较结论性的东西说一下,再把比如为什么这么做不那么做之类的问题解决一下
首先设a=ap*2+aq(aq=0或1) b=bp*2+bq (bq=0或1)(这样就做到了类似数位的思想)
那么u=a+b≤n,也就是(ap+bp)*2+(aq+bq)≤n
稍微变一下就是
ap+bp≤(n-aq-bq)/2
这里的aq与bq只能取0或1
所以当aq+bq=0时,ap+bp≤n/2 叫他方案A吧
当aq+bq=1时,ap+bp≤(n-1)/2 叫他方案B吧
当aq+bq=2时,ap+bp≤(n-2)/2 叫他方案C吧
那么,dp[n]其实就是这三种可能情况下方案数的总和(不能有重复),注意这里的方案数是(a+b,a^b)的方案数
(a+b,a^b)带入一下就是((ap+bp)*2+aq+bq,(ap^bp)*2+aq^bq)
首先,这三种类别之间是不会有重复的(a+b,a^b)产生
首先方案A,C与方案B显然不会重合,因为他们a+b%2不同,所以a+b自然也不会相同
那么重点就是A与C为什么不会重合,先说一下为什么好像可能会重合,因为他们a+b%2都是0
那么什么情况下他们会重合呢?
ap+bp=ap'+bp'+1 且 ap^bp=ap'^bp' 时 会出现重合,那么这种情况存在吗?
答案是显然不存在
你可以带入上面那个有点用的式子
a+b=(a&b)*2+a^b
显然稍微消一下就能得到 (ap&bp)*2=(ap'&bp')*2+1 这显然是不可能的
所以现在我们证明了这三种方案是不重合的
然后就是保证这三种方案内部不重合的方案数如何求出了
其实也很简单,因为他们的末尾已经知道了,所以原来要求的(a+b,a^b)不重复其实就变成了(ap+bp,ap^bp)不重复了
那么也就是说上面三种方案可以直接改写成下面这样
A:dp[n/2]
B:dp[(n-1)/2]
C:dp[(n-2)/2]
所以dp[n]=dp[n/2]+dp[(n-1)/2]+dp[(n-2)/2]
式子总算推完了,然后就是一些小问题
比如,这显然是要用记忆化,但这样会不会超空间以及超时间呢?
这其实也很容易推,n/2,(n-1)/2,(n-2)/2显然会有两种相差为1的结果,可以设为x,x-1
这两种结果又有x/2,(x-1)/2,(x-2)/2,(x-3)/2,也就是2或3种结果,如果是3种结果的话,设为x,x-1,x-2
那么就有x/2,(x-1)/2,(x-2)/2,(x-3)/2,(x-4)/2,也就是3种结果。。。。。。
所以其实dp[n]最多也就是2+2+3+3+3+3...只需要lgn*3个也就是最多180多个
但他们的下标却是ll级别的,所以要用map
还有一个问题就是,为什么要用二进制,比如用三进制,aq+bq分0,1,2,3,4一共5类不行吗?
不行,因为只能从a+b,a^b得到ap+bp,ap^bp(二进制),而得不到三进制的结论
代码:
#include<cstdio> #include<map> using namespace std; #define ll long long const int mod=1e9+7; map<ll,ll> dp; ll dfs(ll x) { if(dp[x]) return dp[x];return dp[x]=(dfs(x/2)+dfs((x-1)/2)+dfs((x-2)/2))%mod; } int main() { ll n; scanf("%lld",&n); dp[0]=1ll;dp[1]=2ll; printf("%lld",dfs(n)); return 0; }