2018 ACM 国际大学生程序设计竞赛上海大都会赛-J:Beautiful Numbers(数位DP)

链接:https://www.nowcoder.com/acm/contest/163/J

时间限制:C/C++ 8秒,其他语言16秒
空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld
题目描述
NIBGNAUK is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by the sum of its digits.
We will not argue with this and just count the quantity of beautiful numbers from 1 to N.
输入描述:
The first line of the input is T ( 1 T 100 ) , which stands for the number of test cases you need to solve.
Each test case contains a line with a positive integer N ( 1 N 10 12 ) .
输出描述:
For each test case, print the case number and the quantity of beautiful numbers in [ 1 , N ] .
示例1
输入
2
10
18
输出
Case 1: 10
Case 2: 12

思路:数位DP。 d [ i ] [ j ] [ k ] [ s u m ] 表示数位总和为 s u m ,前 i 位的数位和是 k ,且前 i 位的值 % s u m = j 的数的个数。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
int p[14];
unsigned d[14][109][109][109];//我是用的for循环来求答案,开long long会爆内存。。
ll f[15];
ll cal(ll x)
{
    if(x<=9)return x;
    int n=0;
    while(x)p[n++]=x%10,x/=10;n--;
    ll ans=9;
    for(int i=n-1;i>=1;i--)
    for(int j=1;j<109;j++)
    {
        ans+=d[i][0][j][j]-d[i-1][0][j][j];
    }
    ll sum=0,res=0;
    for(int i=n;i>=1;i--)
    {
        if(i==n)
        {
            for(int j=1;j<p[i];j++)
            for(int dig=j;dig<109;dig++)
            {
                if(d[i-1][(dig-j*f[i]%dig)%dig][dig-j][dig]==0)continue;
                ans+=d[i-1][(dig-j*f[i]%dig)%dig][dig-j][dig];
            }
        }
        else
        {
            for(int j=0;j<p[i];j++)
            for(int dig=j+sum;dig<109;dig++)
            {
                ans+=d[i-1][(dig-(res%dig+j*f[i]%dig)%dig)%dig][dig-j-sum][dig];
            }
        }
        res+=p[i]*f[i];
        sum+=p[i];
    }
    for(int i=0;i<p[0];i++)
    {
        if((res+i)%(sum+i)==0)ans++;
    }
    res+=p[0];
    sum+=p[0];
    return ans+(res%sum==0);
}
int main()
{
    f[0]=1;
    for(int i=1;i<=13;i++)f[i]=f[i-1]*10;
    //for循环预处理
    memset(d,0,sizeof d);
    for(int i=0;i<=13;i++)
    {
        if(i==0)
        {
            for(int j=0;j<=9;j++)
            for(int dig=max(1,j);dig<109;dig++)d[i][j%dig][j][dig]=1;
            continue;
        }
        for(int j=0;j<=9;j++)          //枚举当前位放的数
        for(int dig=1;dig<109;dig++)   //枚举总数位和
        for(int res=0;res<dig;res++)   //枚举前i位的值%dig后的值
        for(int sum=0;sum+j<=dig;sum++)//枚举前i位的数位和
        {
            if(d[i-1][res][sum][dig]==0)continue;
            d[i][(j*f[i]%dig+res)%dig][sum+j][dig]+=d[i-1][res][sum][dig];
        }
    }
    int T,cas=1;
    cin>>T;
    while(T--)
    {
        ll n;
        scanf("%lld",&n);
        if(n==1e12)//n=1e12的时候会爆unsigned 特殊处理一下
        {
            printf("Case %d: 45975917532\n",cas++);
            continue;
        }
        printf("Case %d: %lld\n",cas++,cal(n));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mitsuha_/article/details/81486550