题目分析:
首先明确一点,0<=l<=r<=1e9 l*r不会爆long long
所以尽管开long long就ok
由 a+b=a^b 异或是不进位的加法
如果要满足这样的性质,那么得确定一点,只要不两个位置同时为1就ok
对于这种数位统计类的问题我们一般用数位dp来做
即保存好状态,分别确定从[1,r]和[1,l-1]中满足情况的数,然后利用容斥原理来减去,
这样就可知道[l,r]区间内满足情况的的数的个数
为什么要这样搞呢???????
因为这个东西的本质是暴力穷举,即从数位高的向数位低的搜索,这样其实等效与一次暴力
枚举??????
只是我们可以设置状态,在pos以前满足一些条件的话??????
我们就可以把接下来的存下来了,就直接输出就可
这样可以看作是一次动态规划的过程???
其实本质是记忆化搜素
但是我们发现一个点,这个有两个操作数,那么就变成二维的一个操作了??????
我们可以继续用容斥的思想来搞
那么[l,r]区间满足条件的画就有 f(r,r)-2*f(l-1,r)+f(l-1,l-1)
dp的过程这个是100组,而且每次都会换不同的[l,r],建议采用把限制条件也加入的做法
这样可以快点,不会超时
如果这个i的当前为1 且j的当前为1 那么就不往下操作就可。
最后穿完所有的数还合法的方案就+1
记录一下方案数即可
/* 题目分析: 首先明确一点,0<=l<=r<=1e9 l*r不会爆long long 所以尽管开long long就ok 由 a+b=a^b 异或是不进位的加法 如果要满足这样的性质,那么得确定一点,只要不两个位置同时为1就ok 对于这种数位统计类的问题我们一般用数位dp来做 即保存好状态,分别确定从[1,r]和[1,l-1]中满足情况的数,然后利用容斥原理来减去, 这样就可知道[l,r]区间内满足情况的的数的个数 为什么要这样搞呢??????? 因为这个东西的本质是暴力穷举,即从数位高的向数位低的搜索,这样其实等效与一次暴力 枚举?????? 只是我们可以设置状态,在pos以前满足一些条件的话?????? 我们就可以把接下来的存下来了,就直接输出就可 这样可以看作是一次动态规划的过程??? 其实本质是记忆化搜素 但是我们发现一个点,这个有两个操作数,那么就变成二维的一个操作了?????? 我们可以继续用容斥的思想来搞 那么[l,r]区间满足条件的画就有 f(r,r)-2*f(l-1,r)+f(l-1,l-1) dp的过程这个是100组,而且每次都会换不同的[l,r],建议采用把限制条件也加入的做法 这样可以快点,不会超时 如果这个i的当前为1 且j的当前为1 那么就不往下操作就可。 最后穿完所有的数还合法的方案就+1 记录一下方案数即可 */ #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> #include <bitset> typedef long long ll; using namespace std; ll dp[37][2][2]; int t; ll l,r,L,R; ll dfs(int pos,int lim_x,int lim_y){ if(pos==-1) return 1; if(dp[pos][lim_x][lim_y]!=-1) return dp[pos][lim_x][lim_y]; int up_x=lim_x?((int)L>>pos)&1:1; int up_y=lim_y?((int)R>>pos)&1:1; ll ans=0; for(int i=0;i<=up_x;i++){ for(int j=0;j<=up_y;j++){ if(!(i&j)){ ans+=dfs(pos-1,lim_x&(i==up_x),lim_y&(j==up_y)); } } } dp[pos][lim_x][lim_y]=ans; return ans; } ll solve(ll st,ll ed){ if(st<0) return 0; L=st;R=ed; memset(dp,-1,sizeof(dp)); int cnt=0; while(ed){ed/=2;cnt++;} return dfs(cnt-1,1,1); } int main(){ scanf("%d",&t); while(t--){ scanf("%lld%lld",&l,&r); printf("%lld ",solve(r,r)-2*solve(l-1,r)+solve(l-1,l-1)); } return 0; }