SPOJBALNUM——数位dp+状压

题目链接:https://vjudge.net/problem/SPOJ-BALNUM

题目大意:

求[l,r]区间的平衡数的个数。

平衡数:所有数位中每一个奇数出现偶数次,每一个偶数出现奇数次。

题解:

定义flag为0表示未出现,1表示出现奇数次,2表示出现偶数次。

所以我们可以把0-9的所有数状压成一个10位的三进制数state,这个状态记录每个数的出现情况。

定义状态为dp[pos][state]表示枚举到pos位,状态为state。

每当枚举下一个数位时,更新枚举数位对状态的影响就行了。

最后返回时再判断是否符合条件就行了。

代码实现:

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define PI atan(1.0) * 4
#define E 2.718281828
#define rp(i, s, t) for (register int i = (s); i <= (t); i++)
#define RP(i, t, s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define pii pair<int, int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
int a[20],num;
ll dp[20][60005];
int judge(int state){
    int digit[20];
    rp(i,0,9){
        digit[i]=state%3;
        state/=3;
    }
    rp(i,0,9){
        if(digit[i]!=0){
            if(i%2==0&&digit[i]==2) return 0;
            if(i%2==1&&digit[i]==1) return 0;
        }
    }
    return 1;
}
int getnum(int x,int state){
    int digit[20];
    rp(i,0,9){
        digit[i]=state%3;
        state/=3;
    }
    if(digit[x]==0) digit[x]=1;
    else digit[x]=3-digit[x];
    int res=0;
    RP(i,9,0){
        res*=3;
        res+=digit[i];
    } 
    return res;
}
ll dfs(int pos,int state,int lead,int limit){
    if(pos==-1) return judge(state);
    if(!lead&&!limit&&dp[pos][state]!=-1) return dp[pos][state];
    int up=limit?a[pos]:9;
    ll ans=0;
    rp(i,0,up) 
        ans+=dfs(pos-1,(lead&&i==0)?0:getnum(i,state),lead&&i==0,limit&&i==a[pos]);
    if(!limit&&!lead) return dp[pos][state]=ans;
    return ans;
}
ll solve(ll x){
    num=0;
    while(x) a[num++]=x%10,x/=10;
    return dfs(num-1,0,1,1);
}
int main(){
    mst(dp,-1);
    int T=read();
    ll l,r;
    while(T--){
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",solve(r)-solve(l-1));
    }
    // printf("\n");
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值