思路:
如果算上i=0&&j=0的情况,其实就是求[0,x][0,y]之间任意两个整数中,满足i&j=0的个数乘以log2(i+j)+1取下整数。因为数据范围到了1e9并且测试样例1e5。所以联想到对位进行操作,于是考虑到---数位dp。
数位dp: https://blog.csdn.net/m0_50623076/article/details/111564154
这个题模板题,赛场上没有想到,赛场下后悔不已。
根据题意进行明确,i&j=0的情况,意味着相加不会进位。Log2(i+j)+1向下取整,意味着之和i、j中最长len值相等。
状态:当前位数len,x的上限标记sx,y的上限标记sy,前导零zeros;
dp[len][sx][sy][zeros];
相较于模板,在这里不需要进行(!limit).那是为了防止高位限制导致后面出现高位不限制而多存在的情况。在这里我们直接让它进入了dp中,也就是sx sy。
再者:ans注意判断是否为最高位,因为这个不单单是统计个数。还有log2(i+j)+1!
代码:(内含注释)
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e6+50;
int t;
int X,Y;
ll dp[35][2][2][2];//位数 x的状态 y的状态 之前是否一直为0
//dp[len][sx][sy][zeros]在长度为len,sx,sy限制、zeros是否为最高位的情况下符合情况的个数。
//返回的是符合情况的结果(已×log(i+j)+1)。
ll x[35],y[35];
ll mod = 1e9+7;
//返回的是对于长度为len的,sx sy对应 x y 高位限制的情况下,zeros是否前面全为零
//即这个len是最高位,有多少种情况符合,返回的是符合情况的结果(已×log(i+j)+1)。
ll dfs(int len,int sx,int sy,int zeros){
//到了len=0也符合题意,这时候完成了一种情况 return 1;
if(len == -1) return 1;
//如果曾经记录了这个情况dp,直接返回。
if(dp[len][sx][sy][zeros] != -1) return dp[len][sx][sy][zeros];
//如果sx=0意味着前面有的数字有的取得并不是高位,这时候此位大多可取到1 sy同理。
int mx = (sx?x[len]:1);
int my = (sy?y[len]:1);
ll ans = 0;
//枚举位的情况
for(int i = 0;i <= mx;i++){
for(int j = 0;j <= my;j++){
if(i&j) continue;
int cnt = 1;
if(zeros&&(i||j)) cnt = len+1;
//如果前面全是0,此位出现1,证明为最高位 log(i+j)+1为len+1。
ans=(ans+dfs(len1,sx&&i==mx,sy&&j==my,zeros&&!i&&!j)*cnt%mod)%mod;
}
}
return dp[len][sx][sy][zeros] = ans;
}
ll solve(){
memset(dp,-1,sizeof(dp));
int len = 0;
while(X||Y){
x[len] = (X&1);
y[len] = (Y&1);
X>>=1;
Y>>=1;
len++;
}
//最高位为len-1位
return dfs(len-1,1,1,1);
}
int main(){
cin >> t;
while(t--){
cin >> X >>Y;
//i=0,j=0的情况删除。
cout << (solve()-1+mod)%mod <<endl;
}
return 0;
}
借鉴于:https://blog.csdn.net/weixin_44178736/article/details/111179926