ATB138F Coincidence 学习笔记
ATB138F Coincidence 学习笔记
题意简述
求满足 \(L\le x\le y\le R,y\bmod x=y\oplus x\) 的 \((x,y)\) 的数量。答案对 \(10^9+7\) 取模。
\(1\le L\le R\le 10^{18}\)。
做法解析
这个数据范围肯定是数位dp了。模和异或,看起来没什么关联,怎么办?别被吓倒了,分析性质。
先把这个不顺眼的 \(y\bmod x\) 转写成 \(y-k\times x\),此时我们发现 \(k\) 只有等于 \(1\) 才可能有贡献:假如 \(k\ge 2\),就说明 \(x\times 2\le y\),就说明二进制下 \(y\) 的位数比 \(x\) 多,此时 \(x\oplus y\) 在比 \(x\) 最高位更高的地方就肯定有 \(1\),然而又有 \(y\bmod x<x\),所以 \(k\ge 2\) 时不可能有 \(y\bmod x=y\oplus x\)。
所以 \(k=1\) 即 \(y\bmod x=y-x\)。我们又知道 \(y\oplus x\) 可以看作不退位减法,所以要想 \(y-x=y\oplus x\),二进制下就不能出现任何退位。也就是说二进制下 \(x\) 得是 \(y\) 的子集:\(x\) 填了 \(1\) 的位 \(y\) 也都填了。
综上,只有在题目条件基础上满足 \((x<y<2\times x)\) 同时 \(x\) 的二进制表示是 \(y\) 的子集的 \((x,y)\) 才会贡献答案。
现在可以开始数位dp了。我们枚举每一个 \(x,y\) 共有的最高位并跑记忆化搜索,当然,状态要包含是否贴到上下界。
代码实现
一只记搜。如果忘了记搜数位dp不妨回来看看。
#include <bits/stdc++.h>
using namespace std;
namespace obasic{
typedef long long lolo;
template <typename _T>
void readi(_T &x){
_T k=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
x*=k;return;
}
template <typename _T>
void writi(_T x){
if(x<0)putchar('-'),x=-x;
if(x>9)writi(x/10);
putchar(x%10+'0');
}
};
using namespace obasic;
const int Mod=1e9+7;
lolo L,R;int lenl,ln[70],lenr,rn[70];
lolo dp[70][2][2],ans;
lolo dfs(int d,int lf,int rf){
if(!d)return 1;
lolo &u=dp[d][lf][rf];
if(~u)return u;u=0;
int lb=lf?ln[d]:0;
int rb=rf?rn[d]:1;
for(int i=lb;i<=rb;i++){
for(int j=lb;j<=i;j++){
(u+=dfs(d-1,lf&(j==lb),rf&(i==rb)))%=Mod;
}
}
return u;
}
int main(){
memset(dp,-1,sizeof(dp));
readi(L),readi(R);
for(;L;L>>=1)ln[++lenl]=L&1;
for(;R;R>>=1)rn[++lenr]=R&1;
for(int i=lenl;i<=lenr;i++)(ans+=dfs(i-1,(i==lenl),(i==lenr)))%=Mod;
writi(ans);
return 0;
}
反思总结
\(y\bmod x\) 可以转写成 \(y-k\times x\);
当模运算碰到位运算时,由于位运算的不进退位性,通常对 \(k=1\) 和 \(k\ge 2\) 进行分类讨论。
浙公网安备 33010602011771号