Ryoku 与最初之人笔记
题目描述
求满足 \(a\equiv b\pmod {a \text{ xor } b}\),且 \(a,b\) 均为小于等于 \(n\) 的非负整数,\(a<b\),的有序二元组 \((a,b)\) 个数。
\(n \leqslant 10^{18}\) 。
思路点拨
这里提供一种数位dp的做法。
考虑 \(a\equiv b\pmod {a \text{ xor } b}\) 的含义是什么?我们可以将原式理解为:
由于 \(b-a \leqslant a\oplus b\) 并且 \(k_2-k_1\) 为正整数,所以应该有 \(a\oplus b=b-a\)。这个式子等价于 \(a\&b=a\),其中 \(\&\) 是指位运算中的与。
我们考虑使用数位dp结局这个问题。我们定义 \(dp_{i,j=0/1}\) 表示考虑到二进制位的第 \(i\) 位,目前是否超出了 \(n\) 二进制表示的前 \(j\) 位。转移我们分类讨论:
如果 \(n\&2^i\),那么:
-
\(dp_{i,0}=dp_{i-1,0}\times 3+dp_{i-1,1}\)。因为这一位可以填 \(0,1\),所以对于数字 \(a,b\) 而言,这一数位 \(a,b\) 两数可以使用的组合为 \((0,0),(0,1),(1,1)\)。\(dp_{i-1,1}\) 则是代表如果填 \((0,0)\) ,可以消除二进制位的限制。
-
\(dp_{i,1}=dp_{i-1,1}\times 2\) 。为了保持超出二进制位,所以第 \(i\) 位 \(a,b\) 至少有一个填 \(1\) ,组合可以有 \((0,1),(1,1)\) 。
如果没有满足 \(n \& 2^i\) ,也就是说目前这一位在不超出二进制限制的情况下只可以填 \(0\) 。
-
\(dp_{i,0}=dp_{i-1,0}\) 。不超出限制只可以填 \((0,0)\) 。
-
\(dp_{i,1}=dp_{i-1,0}\times 2+dp_{i-1,1}\times 3\) 。可以从 \(dp_{i-1,0}\) 转移是因为可以让 \(a,b\) 在第 \(i\) 位至少填一个 \(1\) 可以使其超出限制,所以可以选择 \((0,1),(1,1)\) ;为什么可以从 \(dp_{i-1,1}\) 转移上面解释过了。
初始化,如果 \(n\) 是奇数,那么 \(dp_{0,0}=3\) 。
如果 \(n\) 是偶数,那么 \(dp_{0,0}=0,dp_{0,1}=2\) 。
可能比较抽象,如果不理解可以自己手玩一下。时间复杂度 \(O(\log n)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=64,N=60,mod=1e9+7;
int n,dp[MAXN][2];
signed main(){
cin>>n;
if(n&1)
dp[0][0]=3;
else dp[0][0]=1,dp[0][1]=2;
for(int i=1;i<=N;i++){
if(n&(1ll<<i)){
dp[i][0]=(dp[i-1][0]*3+dp[i-1][1])%mod;
dp[i][1]=(dp[i-1][1]*2)%mod;
}
else{
dp[i][0]=dp[i-1][0];
dp[i][1]=(dp[i-1][0]*2+dp[i-1][1]*3)%mod;
}
}
cout<<(dp[N][0]-(n+1)%mod+mod)%mod;
return 0;
}