码迷,mamicode.com
首页 > 其他好文 > 详细

数位DP

时间:2018-05-13 13:41:58      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:产生   define   从零开始   i++   pac   pre   计数   div   +=   

基本思想在于将一个数拆分成每一位的数字,将总问题转化为子问题。

例:求[a,b]中各个数字出现次数。 (luogu2602 数字计数)

1.发现在i位数中(不考虑前导零),每一个数字出现的次数都相同。f[i]=f[i-1]*10+10^(i-1);

2.在ABCD中,将其拆为:A000+B00+C0+D; A000中,考虑后三位每个数字出现次数都为:A*f[3];

3.再加上第一位上小于A(从零开始,算前导零的时候会减去)的数出现了10^3次

4.然而,后面的000~BCD中,每一个数的产生也伴随着A的一次出现,所以A出现次数单独加上BCD*1;

5.处理前导零; 仅在开始填0~9999(len-1个)时会产生。 零的数量单独减去10^(len-1)+10^(len-2)+...+10+1;

不用写两个query, 对于数组,传一个*就好。 详见代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=20;
ll a,b;
ll cnta[20],cntb[20];
ll f[20],ten[20];
void query(ll x,ll *cnt)
{
    int len=0;
    int num[20];
    ll k=x;
    while(k)
    {
        num[++len]=k%10;
        k/=10;  
    }
    for(int i=len;i>=1;i--)
    {
        for(int j=0;j<=9;j++)
         cnt[j]=cnt[j]+f[i-1]*num[i];
        for(int j=0;j<num[i];j++)
         cnt[j]+=ten[i-1];
        ll num2=0;
        for(int j=i-1;j>=1;j--)
        {
            num2=num2*10+num[j];    
        }
        num2++;
        cnt[num[i]]+=num2;
        cnt[0]-=ten[i-1];
    }
}
int main()
{
    scanf("%lld%lld",&a,&b);
    ten[0]=1;
    for(int i=1;i<=15;i++)
    {
        f[i]=f[i-1]*10+ten[i-1];
        ten[i]=ten[i-1]*10; 
    }
    query(a-1,cnta);
    query(b,cntb);
    for(int i=0;i<=9;i++)
     printf("%lld ",cntb[i]-cnta[i]);
    return 0;
}

luogu(P2602)

数位DP

标签:产生   define   从零开始   i++   pac   pre   计数   div   +=   

原文地址:https://www.cnblogs.com/Miracevin/p/9031693.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!