题目链接:https://codeforces.com/contest/1245/problem/F
题目大意:
l<=a<=r,l<=b<=r,问a+b=a^b的对数
题目思路:
qsc题解视频:传送门
这道题,就差标题后面画个括弧里面写上数位DP模板题了。。一眼数位dp,然而太久太久没写数位dp了,完全忘记了怎么写。。。。。。。。
真的是非常简单的数位dp入门题,首先a+b=a^b等价于a&b=0,就是找到范围内a&b=0的数量。对于数位DP来说,确定0~r是比较简单的,所以通常对于l~r都是通过 s o l v e ( r ) − s o l v e ( l − 1 ) solve(r)-solve(l-1) solve(r)−solve(l−1)得到,但是这个题涉及两个量,这也难不倒我们!使用一个小容斥, s o l v e ( l , r ) solve(l,r) solve(l,r)表示a在0~l范围内,b在0~r范围内的情况数。那么答案就是 s o l v e ( r , r ) − 2 ∗ s o l v e ( l − 1 , r ) + s o l v e ( l − 1 , l − 1 ) solve(r,r)-2*solve(l-1,r)+solve(l-1,l-1) solve(r,r)−2∗solve(l−1,r)+solve(l−1,l−1),然后怎么solve呢?记忆化搜索, d f s ( l , r , p o s , a , b ) dfs(l,r,pos,a,b) dfs(l,r,pos,a,b)分别表示第一个数范围0~l,第二个数0~r,然后当前枚举到哪一位,a表示第一个数枚举的时候有没有限制(之前全都一样说明现在还是得被这一位限制,比如对于123来说,现在假如枚举12,就说明有限制,最后一位只能0~3,但是假如说枚举的是11,那么最后一位就随便来,就能是0~9)。然后就中规中矩的干活咯,看看后面的情况。不过我已经菜到忘记更新哪个变量,返回哪个变量。。唉,蓝瘦
以下是代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
ll dp[40][2][2];
ll dfs(int l,int r,int pos,int a,int b){
if(pos==-1)return 1;
if(dp[pos][a][b]!=-1)return dp[pos][a][b];
int la=1,lb=1;
if(a)la=(l>>pos)&1;
if(b)lb=(r>>pos)&1;
dp[pos][a][b]=0;
rep(i,0,la){
rep(j,0,lb){
if(i&j)continue;
dp[pos][a][b]+=dfs(l,r,pos-1,a&(i==la),b&(j==lb));
}
}
return dp[pos][a][b];
}
ll solve(int l,int r){
if(l<0)return 0;
memset(dp,-1,sizeof(dp));
return dfs(l,r,30,1,1);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--){
int l,r;
cin>>l>>r;
cout<<solve(r,r)-2*solve(l-1,r)+solve(l-1,l-1)<<endl;
}
return 0;
}