• [ZJOI2010] 数字统计


    [ZJOI2010] 数字统计

    题目

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

    INPUT

    输入文件中仅包含一行两个整数a、b,含义如上所述

    OUTPUT

    输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

    SAMPLE

    INPUT

    1 99

    OUTPUT

    9 20 20 20 20 20 20 20 20 20

    解题报告

    第二道数位$dp$,找着点感觉了?

    首先,我们预处理出来从低向高位数第$i$位数,每个数码出现的次数,递推式很简单

    $$f[i]=10*f[i-1]+10^{i-1}$$

    我们分两部分考虑即可,第$i$位为该数字的数有$10^{i-1}$个,后$i-1$位数该该数字出现的次数为$f[i-1]$,前面的数共有$10$种可能(允许前导0),故$f[i]=10*f[i-1]+10^{i-1}$

    然后考虑如何统计答案。

    对于低于该数位数的数,我们可以去除前导零,对上式进行变形(具体式子见代码),求和即可

    对于位数等于该数位数的数,我们可以逐位枚举统计,利用处理出来的$f$数组和$10^{i}$进行转移即可

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 using namespace std;
     5 typedef long long L;
     6 L a,b,f[20],pw[20],ans[20];
     7 int num[40],top;
     8 inline void solve(L x,int flag){
     9     if(!x)return;
    10     top=0;L ret(x);
    11     while(x){num[++top]=x%10;x/=10;}
    12     for(int i=1;i<top;++i)
    13         for(int j=0;j<=9;++j)
    14             ans[j]+=flag*(9*f[i-1]+(j?pw[i-1]:0));
    15     for(int i=top;i;--i){
    16         ret-=num[i]*pw[i-1];ans[num[i]]+=(ret+1)*flag;
    17         for(int j=(i==top);j<num[i];++j)ans[j]+=pw[i-1]*flag;
    18         for(int j=0;j<=9;++j)ans[j]+=f[i-1]*(num[i]-(i==top))*flag;
    19     }
    20 }
    21 int main(){
    22     freopen("countzj.in","r",stdin);freopen("countzj.out","w",stdout);
    23     scanf("%lld%lld",&a,&b);
    24     pw[0]=1;for(int i=1;i<=13;++i)f[i]=10*f[i-1]+pw[i-1],pw[i]=pw[i-1]*10;
    25     solve(b,1);solve(a-1,-1);
    26     printf("%lld",ans[0]);for(int i=1;i<=9;++i)printf(" %lld",ans[i]);
    27 }
    View Code
  • 相关阅读:
    微信小程序通过CODE换取session_key和openid
    BZOJ1497[NOI2006]最大获利——最大权闭合子图
    Leetcode#149 Max Points on a Line
    IO流总结
    java基础比较好的笔记总结
    chrome web开发工具
    day 01 常量 注释 int(整型) 用户交互input 流程控制语句if
    10.2:异或树经验
    搜索+保存路径
    搜索+简单dp
  • 原文地址:https://www.cnblogs.com/hzoi-mafia/p/7766435.html
Copyright © 2020-2023  润新知