Sum of Log(icpc上海站2020) 数位dp

上海站其余题目(点击进入)

思路:

       如果算上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

      

             

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值