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

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

题目

地址:https://ac.nowcoder.com/acm/contest/9925/C
在这里插入图片描述
在这里插入图片描述

题目大意

寻找对于任意二元组 ( 0 ∼ x , 0 ∼ y ) (0\sim x, 0\sim y) (0x,0y),除了 ( 0 , 0 ) (0, 0) (0,0),并且满足 i & j = 0 i \& j=0 i&j=0的所有 l o g 2 ( i + j ) + 1 log_2(i + j) +1 log2(i+j)+1向下取整的和相加的最终结果。

思路

  • 考虑到x,y的范围到达了1e9,还有t的1e5,一次需要在1e3时间内解决,无法通过枚举解决。那么类似的问题就剩两种可能性较大的解法,数学得到式子能够直接解决问题,第二种数位dp。
  • 熟悉数位dp的话应该很快能看出来数位dp,由于要求 i & j = 0 i \& j=0 i&j=0,枚举数位的时候按照二进制枚举,i+j最高位永远不会超过进位,那么每一对值贡献的结果就是最高位的数位,那么直接数位dp就行了。
  • 数位dp的话有一个小变形,常规的数位dp枚举一个数,这里枚举两个数,那么要通过两重for循环分别枚举两个数的同一位置的值。
  • 设置状态时,如果设置的是一个数的最高位,那么会超时。。。
    需要小优化,设置是否存在最高位就可以了,倒时候在转移时,如果刚好出现最高位,直接将接下来的答案乘最高位即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int x[40], y[40];
long long dp[40][2][2][2];
long long res;
long long dfs(int pos, bool l1, bool l2, bool fs){
    if (pos == -1) return 1;
    if (dp[pos][l1][l2][fs] != -1) return dp[pos][l1][l2][fs];
    int up1 = l1 ? x[pos] : 1;
    int up2 = l2 ? y[pos] : 1;
    long long ans = 0;
    for (int i = 0; i <= up1; i ++){
        for (int j = 0; j <= up2; j ++){
            if (i & j) continue;
            int fval = 1;
            bool f = 0;	//如果出现了下面情况,之后递归fs取1
            if ((i || j) && fs == 0) fval = pos + 1, f = 1;;
            ans = (ans + dfs(pos - 1, l1 && i == up1, l2 && j == up2, fs || f) * fval) % mod;
        }
    }
    if (dp[pos][l1][l2][fs] == -1)  dp[pos][l1][l2][fs] = ans;
    return ans;
}
void solve(int a, int b){
    int idx1 = 0, idx2 = 0;
    while (a){
        x[idx1 ++] = a % 2;
        a /= 2;
    }
    while (b){
        y[idx2 ++] = b % 2;
        b /= 2;
    }
    res = dfs(max(idx1, idx2) - 1, 1, 1, 0) % mod;
}
int main()
{
    int t; cin >> t;
    while (t --){
        memset(dp, -1, sizeof dp);
        memset(x, 0, sizeof x);
        memset(y, 0, sizeof y);
        int a, b; cin >> a >> b;
        solve(a, b);
        cout << (res - 1 + mod) % mod << endl; //减去(0,0)
    }
     return 0;
}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值