[BZOJ1833][ZJOI2010]count 数字计数(数位dp)

245 篇文章 0 订阅
211 篇文章 0 订阅

题目描述

传送门

题解

首先枚举要计算个数的数字x
f(i,j,k,0/1,0/1,0/1)表示前i位,第i位填数字j,是x的数字有k个,是否卡下界L,是否卡上界R,1~i位是否都是前导0的数字个数。
那么答案即为f(n,j,k,0/1,0/1,0)*k
注意转移的时候要分别处理是前导0和不是前导0的情况。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long

int n;
int a[20],b[20];
LL L,R,f[20][20][20][2][2][2],ans;

int main()
{
    scanf("%lld%lld",&L,&R);
    while (L)
    {
        a[++a[0]]=L%10;
        L/=10;
    }
    while (R)
    {
        b[++b[0]]=R%10;
        R/=10;
    }
    n=max(a[0],b[0]);
    for (int i=1;i<=n/2;++i) swap(a[i],a[n-i+1]);
    for (int i=1;i<=n/2;++i) swap(b[i],b[n-i+1]);
    for (int x=0;x<=9;++x)
    {
        memset(f,0,sizeof(f));
        for (int i=a[1];i<=b[1];++i) f[1][i][i==x&&x!=0?1:0][i==a[1]?1:0][i==b[1]?1:0][i==0?1:0]=1;
        for (int i=1;i<n;++i)
            for (int j=0;j<=9;++j)
                for (int k=0;k<=n;++k)
                    for (int c=0;c<=1;++c)
                        for (int d=0;d<=1;++d)
                            for (int e=0;e<=1;++e)
                            {
                                int l,r;
                                if (c&&d) l=a[i+1],r=b[i+1];
                                else if (!c&&!d) l=0,r=9;
                                else if (!c) l=0,r=b[i+1];
                                else if (!d) l=a[i+1],r=9;
                                for (int t=l;t<=r;++t)
                                {
                                    if (!e)
                                    f[i+1][t][t==x?k+1:k][c&(t==a[i+1]?1:0)][d&(t==b[i+1]?1:0)][0]+=
                                    f[i][j][k][c][d][e];
                                    else
                                    f[i+1][t][t==0?0:(t==x?1:0)][c&(t==a[i+1]?1:0)][d&(t==b[i+1]?1:0)][e&(t==0)?1:0]+=
                                    f[i][j][k][c][d][e];
                                }
                            }
        ans=0;
        for (int j=0;j<=9;++j)
            for (int k=1;k<=n;++k)
                for (int c=0;c<=1;++c)
                    for (int d=0;d<=1;++d)
                        ans+=f[n][j][k][c][d][0]*(LL)k;
        printf("%lld%c",ans," \n"[x==9]);
    }
}

总结

①写这个要细心。每一个状态一定要表示得精确。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值