Sum of Log ICPC上海区域赛 数位dp 双线程

Sum of Log ICPC上海区域赛 数位dp 双线程

题目大意:

思路:

这个是我第一次写的双线程的数位dp,也是第一次碰到的需要在T组内 memset 这个 dp 数组的题目,还是很有意思的,这种题目一般都需要对 limit 进行记忆化来保证时间复杂度。

这个题目状态的定义:

dp[pos][limit1][limit2][zero] :表示对于第pos位,第一个的状态是limit1,第二个的状态是limit2,前导零的状态是zero ,这个 zero 是用来判断最高位的,也可以不用这个来判断,而是手动暴力枚举最高位是X或Y和在哪个位置。

这个题目我主要是通过看题解代码来学习掌握的,之后补一题2020CCPC网络赛 Xor 和后面ICPC济南区域赛的一个数位dp 来巩固

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
ll dp[33][2][2][2];
// dp[pos][limit1][limit2][zero] 表示对于第pos位,第一个的状态是limit1,第二个的状态是limit2,前导零的状态是zero
// 这个zero是为了判断是不是最高位
int a[33],b[33];
ll dfs(int pos,bool limit1,bool limit2,bool zero){
//    printf("pos = %d limit1 = %d limit2 = %d zero = %d\n",pos,limit1,limit2,zero);
    if(pos==-1) return 1;
    if(dp[pos][limit1][limit2][zero]!=-1) return dp[pos][limit1][limit2][zero];
    int up1 = limit1?a[pos]:1;
    int up2 = limit2?b[pos]:1;
    ll ans = 0;
    for(int i=0;i<=up1;i++){
        for(int j=0;j<=up2;j++){
            if(i&j) continue;
            ll res = 1;
            if(!zero&&(i||j)) res = pos + 1;
            ans = (ans + dfs(pos-1,limit1&&i==up1,limit2&&j==up2,zero||i||j)*res%mod)%mod;
        }
    }
    return dp[pos][limit1][limit2][zero] = ans;
}
ll solve(){
    int pos = 0,X,Y;
    scanf("%d%d",&X,&Y);
    memset(dp,-1,sizeof(dp));
    while(X||Y){
        a[pos] = X&1;
        b[pos] = Y&1;
        X>>=1,Y>>=1;
        pos++;
    }
    return dfs(pos-1,true,true,false);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ll ans = solve();
        printf("%lld\n",(ans-1+mod)%mod);
    }
    return 0;
}
posted @ 2021-01-18 18:51  EchoZQN  阅读(103)  评论(0编辑  收藏  举报