Gym 102900C. Sum of Log(ICPC2020上海C)数位dp

题目链接
思路
因为前提条件(i)&(j==0),所以i和j的任意一位最高位一定不同,那么(log_2^{i+j})就相当于看i或者j最高位为1在第几位,然后计算贡献。
转化为二进制考虑每一位的情况,数据范围只到1e9,考虑通过数位dp来得到所有方案数。如果没有两个位数不一样的其实可以当作默认把小的那个数前面补齐了前导0.
(dp[i][1/0][1/0]:)在数字n,m的二进制表示下第i位,数字n在这一位是否取到上限的状态,数字m是否取到上限的状态的所有方案数。若有上限的限制就是1,没有上限的限制就是0。可以通过dfs写数位dp的写法来解决这个问题。
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int mod = 1e9 + 7;

const int N = 50;
int dp[N][2][2];
int a[N], b[N], n, m;

LL dfs(int pos, bool x, bool y) {
    if(!pos) return 1;
    if(dp[pos][x][y] != -1) return dp[pos][x][y];
    int up1 = x ? a[pos] : 1;
    int up2 = y ? b[pos] : 1;
    LL sum = 0;
    for(int i = 0; i <= up1; i++) {
        for(int j = 0; j <= up2; j++) {
            if(!(i & j)) {
                sum += dfs(pos - 1, x && i == a[pos], y && j == b[pos]);
                sum %= mod;
            }
        }
    }
    return dp[pos][x][y] = sum;
}

void solve() {
    memset(a, 0, sizeof a);
    memset(b, 0, sizeof b);
    scanf("%d%d", &n, &m);
    int idx1 = 0, idx2 = 0;
    while(n) {
        a[++idx1] = n % 2;
        n /= 2;
    }
    while(m) {
        b[++idx2] = m % 2;
        m /= 2;
    }
    memset(dp, -1, sizeof dp);
    LL res = 0;
    for(int i = 1; i <= max(idx1, idx2); i++) {
        LL cnt = 0;
        if(i <= idx1) cnt += dfs(i - 1, idx1 == i, i - 1 >= idx2);
        if(i <= idx2) cnt += dfs(i - 1, i - 1 >= idx1, idx2 == i);
        res += 1LL * cnt * i % mod;
        res %= mod;
    }
    printf("%lld
", res);
}

int main() {
    // freopen("in.txt", "r", stdin);
    int t; cin >> t; while(t--)
    solve();
    return 0;
}
原文地址:https://www.cnblogs.com/ZX-GO/p/14412945.html