数位dp-AT_abc138_f

为什么大家写的都是记忆化搜索,没人写正向递推()

[原题传送门](https://atcoder.jp/contests/abc138/tasks/abc138_f)

看到数据范围,考虑$O(1)$算法,然而并不可行,所以我们考虑$O(\log n)$算法,就是数位$dp$

考虑将$y \bmod x$ 这一坨东西转换,我们注意到,$x \oplus y \ge y-x$,所以为了满足条件 $y-x \le x \bmod y$,

接着,我们又注意到$x \bmod y = x-[x/y]*y$,易得,当且仅当$[x/y]=1$,即$2*y>x$时,有$y-x \le x \bmod y$,且当$2*y>x$时,$x \bmod y = x-y$,所以我们可以将问题转化为$y-x=x \oplus y$,

可以发现,对于一位的二进制,当$x[i]=1$,$y[i]=1$或$0$,当$x[i]=0$,$y[i]$只能等于$0$,$x$与$y$二进制位数一样,特殊的,为了保证$2*y>x$,对于$x$与$y$的第一位,必须都取$1$

我们可以利用数位$dp$维护该问题


```cpp
#include<bits/stdc++.h> 
using namespace std;
#define int long long
const int mod=1e9+7;
int dp[66][2][2][2];//dp[i][j][k][l]为,当前处理第i位,x取顶的状态是i,y取顶的状态是j,x,y是否都已取值的状态为l 
int l,r;
int cal(int x,int y){
    for(int i=0;i<=60;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) for(int l=0;l<2;l++) dp[i][j][k][l]=0;
    dp[60][1][1][0]=1; 
    for(int i=59;i>=0;i--){
        for(int j=0;j<2;j++){
            for(int k=0;k<2;k++){
                for(int l=0;l<2;l++){
                    bool nowj,nowk;
                    if(j==1&&!((x>>i)&1)){
                        nowj=1;
                    }
                    else nowj=0;
                    if(k==1&&!((y>>i)&1)){
                        nowk=1;
                    }
                    else nowk=0;
                    dp[i][nowj][nowk][l]=(dp[i][nowj][nowk][l]+dp[i+1][j][k][l])%mod;
                    if(nowj==0&&nowk==0) dp[i][j][k][1]=(dp[i][j][k][1]+dp[i+1][j][k][l])%mod;
                    if(nowj==0&&l==1) dp[i][j][nowk][l]=(dp[i][j][nowk][l]+dp[i+1][j][k][l])%mod;
                }
            }
        }
    }
    return dp[0][0][0][1]+dp[0][0][1][1]+dp[0][1][0][1]+dp[0][1][1][1];
}
signed main(){
    cin>>l>>r;
    int add=cal(r,r)%mod,add2=cal(l-1,r)%mod,add3=cal(l-1,l-1)%mod,add4=cal(r,l-1)%mod;
    cout<<(add-add2+add3-add4+mod)%mod<<endl;//做二维差分,得出答案 
}
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值