zoukankan      html  css  js  c++  java
  • 题解 P4999 【烦人的数学作业】

    数位 dp。

    (dp_{q,i})(iin{0,1,2,3,4,5,6,7,8,9}))为 (1sim q)(i) 出现的次数,(1sim q) 的数字和显然就是 (dp_{q,0} imes 0+dp_{q,1} imes 1+cdots+dp_{q,i} imes icdots+dp_{q,9} imes 9)

    所以我们只需要求出 (1sim q)(i) 出现的次数就能解决这个问题了。

    这个问题看起来很好解决,但是注意前导零会影响结果,所以不能有前导零。

    这该怎么办呢?

    有前导零的式子很容易推出。有 (q) 位数字,(i) 数码的出现次数对于 (xin{smid sin mathbb N,10^qle sle10^{q+1}})(f(q,i)) 的数量都是相等的(设 (f(q,i))(q) 位数 (i) 数码的出现次数)。

    具体求法罢,是:
    (egin{cases}f(q,i)=0&q=0\f(q,i)=10f(q-1)+10^{q-1}&q>0end{cases})

    我们考虑减去多余的 (0)

    我们先设数字为 (overline{A_1A_2A_3dots A_n})

    我们首先考虑求 (overline{A_100dots 0}),将 (overline{A_100dots 0}) 分割为区间 ([0000,1000),[1000,2000),dots,[overline{(A_1-1)00dots 0},overline{A_100dots 0})),所以答案就为 (10^{n-1}A_1),注意 (<A_1) 的每个数还出现了 (10^{n-1}) 次,所以要加上。

    首位 (A_1) 出现了 (overline{A_2A_3dots A_n}+1) 次,答案还要加上 (overline{A_2A_3dots A_n}+1)

    当然还需要处理前导 (0),用排列组合算一下会知道 (i)(q) 个前导零的数量就是 (10^q)(qin{smid sinmathbb N,0le sle i-1})),把它们加起来会发现一共出现了 (10^{i-1}+10^{i-2}+...10)(=sumlimits_{k=0}^{i-1}10^k)) 次,减一下即可。

    Code:

    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=51,MOD=1e9+7; //注意能 MOD 的地方都要 MOD,不然会 WA 0pts。
    ll pow10[N],dp[N],a[N],count[N],tmpcount[N],ans;
    // pow10    : 字面意思,10^n
    // dp       : 不考虑前导零的状况
    // count    : 统计 0~9 出现次数
    // tmpcount : 暂时保存 count,用来减
    // ans      : 累加答案
    void init() //预处理 pow10 和 dp。
    {
    	pow10[0]=1;
    	for (int i=1;i<30;i++) dp[i]=(dp[i-1]*10%MOD+pow10[i-1])%MOD,pow10[i]=10*pow10[i-1]%MOD;
    }
    void solve(ll x)
    {
    	int len=0;
    	while (x){a[++len]=x%10;x/=10;} //数位分离
    	for (int i=len;i>=1;i--)        //从高到低遍历
    	{
    		for (int j=0;j<10;j++) count[j]+=dp[i-1]*a[i],count[j]%=MOD;  //分割区间
    		for (int j=0;j<a[i];j++) count[j]+=pow10[i-1],count[j]%=MOD; //加上 10^(n-1)
    		ll lastnum=0;
    		for (int j=i-1;j>=1;j--) lastnum=lastnum*10+a[j],lastnum%=MOD; //求出 A2A3A4...An
    		count[a[i]]+=lastnum+1,count[a[i]]%=MOD;
    		count[0]-=pow10[i-1],count[0]=(count[0]+MOD)%MOD; //减去前导零
    	}
    }
    int main()
    {
    	init();
    	ll l,r,T;
    	cin>>T;
    	for (int q=0;q<T;q++)
    	{
    		ans=0; cin>>l>>r;
    		solve(r); //前缀和思想相减 r 和 l-1。
    		for (int i=0;i<10;i++) (tmpcount[i]=count[i]),count[i]=0; //复制 count,记得清零
    		solve(l-1);
    		for (int i=0;i<10;i++) ans=(ans+i*(tmpcount[i]-count[i]+MOD)%MOD)%MOD,count[i]=0; //累加答案,记得清零 count。
    		cout<<ans<<'
    ';
    	}
    	return 0;
    }
    

    Refence 求数字 (i) 出现的次数

  • 相关阅读:
    Map Wiki -- proposed by Shuo Ren
    Smart Disk -- proposed by Liyuan Liu
    ubuntu 16.04下如何打造 sublime python编程环境
    manjaro linux没有ll等命令的解决办法
    python学习-命名规则
    python-unitetest-unittest 的几种执行方式
    python-pytest学习(一)- 简介和环境准备
    Python+request+unittest学习(一)- 读取文本出现 锘 * 系列乱码错误(UTF-8 BOM问题)的原因及解决方法
    Python+Selenium框架版(十)- unittest执行方法之discover()方法
    Python+Selenium框架版(九)- unittest执行法之makeSuit()
  • 原文地址:https://www.cnblogs.com/CDOI-24374/p/12913558.html
Copyright © 2011-2022 走看看