列表

详情


DP62. [ZJOI2010]COUNT 数字计数

描述

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

输入描述

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

输出描述

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

示例1

输入:

1 99

输出:

9 20 20 20 20 20 20 20 20 20

原站题解

C 解法, 执行用时: 2ms, 内存消耗: 300KB, 提交时间: 2022-01-07

#include <stdio.h>
#include <math.h>

int lengthOf(const char s[]) {
    int i = 0;
    while (s[i] != '\0')
        ++i;
    return i;
}

long long parseLongLong(const char s[]) {
    long long ans = 0;
    int i;
    for (i = 0; s[i] != '\0'; ++i)
        ans = ans * 10 + s[i] - '0';
    return ans;
}

void ll2Str(char dst[], int arr_size, long long num) {
    int i = -1, j;
    while (num > 0) {
        dst[++i] = num % 10 + '0';
        num /= 10;
    }

    if (i == -1)
        dst[++i] = '0';

    for (j = i / 2; j >= 0; --j) {
        char t = dst[j];
        dst[j] = dst[i - j];
        dst[i - j] = t;
    }
    for (++i; i < arr_size; ++i)
        dst[i] = '\0';
}

long long countDigit(char s[], int len, int i, int d) {
    int curr = s[i] - '0', k = len;
    long long ans;
    if (s[i + 1] == '\0')
        return curr < d ? 0 : 1;
    if (s[i] == '0')
        return countDigit(s, len - 1, i + 1, d);

    ans = (long long) pow(10, k - 2) * curr * (k - 1) + countDigit(s, len - 1, i + 1, d);
    if (curr > d)
        ans += (long long) pow(10, k - 1);
    else if (curr == d)
        ans += parseLongLong(s + i + 1) + 1;
    return ans;
}

long long countAll(char s[], int k) {
    int i;
    long long ans = (parseLongLong(s) - (long long) pow(10, k - 1) + 1) * k, m = 9;
    for (i = 1; i < k; ++i) {
        ans += m * i;
        m *= 10;
    }
    return ans;
}

int main() {
    char a[15], b[15];
    scanf("%s %s", a, b);
    int len_a = lengthOf(a), len_b = lengthOf(b);
    ll2Str(a, 15, parseLongLong(a) - 1);

    long long ans[10];
    ans[0] = countAll(b, len_b) - countAll(a, len_a);
    for (int i = 1; i < 10; ++i) {
        ans[i] = countDigit(b, len_b, 0, i) - countDigit(a, len_a, 0, i);
        ans[0] -= ans[i];
    }

    printf("%lld", ans[0]);
    for (int i = 1; i < 10; ++i)
        printf(" %lld", ans[i]);

    return 0;
}

C 解法, 执行用时: 2ms, 内存消耗: 384KB, 提交时间: 2021-12-10

#include <stdio.h>
#include <math.h>

void calcu(long long num , long long *digit,int flag)
{
	if(num < 0) return;
	long long temp_num = num , index = 1;
	
	for(int i=0;i<=temp_num%10;++i)
	{
		digit[i] += temp_num/10 + 1;
	}
	++index;
	
	//if(flag==0)
	//	digit[0] += temp_num%10+1;
	temp_num /= 10;

		
	while(temp_num)
	{
		for(int i=0;i<=temp_num%10;++i)
		{
			if(temp_num <= 9 && i == 0 && flag != 0) continue;
			
			if(i == 0 && flag != 0)
				digit[i] += (temp_num/10>0?(temp_num/10):1) * (long long)pow(10,index-1);
			else
				digit[i] += (temp_num/10>0?(temp_num/10+1):1) * (long long)pow(10,index-1);
			
		}
		++index;
		temp_num /= 10;
	}
}

int main()
{
	long long temp_num_l,num_l,index;
	long long temp_num_r,num_r;
	long long temp_l[100][3]={{0,0,0}};
	long long temp_r[100][3]={{0,0,0}};
	long long digit_l[10]={[0 ... 9] = 0};
	long long digit_r[10]={[0 ... 9] = 0};
	scanf("%lld %lld",&num_l,&num_r);
	//--num_l;
	temp_num_l = num_l;
	temp_num_r = num_r;
	
	index = 1;
	while(temp_num_l)
	{
		temp_l[index][0] = temp_num_l%10;
		temp_l[index][1] = temp_l[index][0] * (long long)pow(10,index-1);
		temp_l[index][2] = num_l - temp_num_l * (long long)pow(10,index-1);
		temp_num_l /= 10;
		++index;
	}
	for(int i=index-1;i>0;--i)
	{
		calcu(temp_l[i][1]-1 , digit_l ,i==index-1);
		digit_l[temp_l[i][0]] += temp_l[i][2]+1;
		if(i!=index-1 && i!=1  && temp_l[i][0] == 1)
			digit_l[0] += temp_l[i][1];
		//printf("0~%lld [%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld]\n",temp_l[i][1],
		//digit_l[0],digit_l[1],digit_l[2],digit_l[3],digit_l[4],digit_l[5],digit_l[6],digit_l[7],digit_l[8],digit_l[9]);
	}
	
	index = 1;
	while(temp_num_r)
	{
		temp_r[index][0] = temp_num_r%10;
		temp_r[index][1] = temp_r[index][0] * (long long)pow(10,index-1);
		temp_r[index][2] = num_r - temp_num_r * (long long)pow(10,index-1);
		temp_num_r /= 10;
		++index;
	}
	for(int i=index-1;i>0;--i)
	{
		calcu(temp_r[i][1]-1 , digit_r ,i==index-1);
		digit_r[temp_r[i][0]] += temp_r[i][2]+1;
		if(i!=index-1 && i!=1  && temp_r[i][0] == 1)
			digit_r[0] += temp_r[i][1];
		//printf("0~%lld [%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld]\n",temp_r[i][1],
		//digit_r[0],digit_r[1],digit_r[2],digit_r[3],digit_r[4],digit_r[5],digit_r[6],digit_r[7],digit_r[8],digit_r[9]);
	}

	for(int i=0;i<10;++i)
		digit_r[i] -= digit_l[i];
		
	while(num_l)
	{
		++digit_r[num_l%10];
		num_l/=10;
	}
	
	for(int i=0;i<10;++i)
		printf("%lld ",digit_r[i]);
	
	return 0;	
} 

C++ 解法, 执行用时: 2ms, 内存消耗: 424KB, 提交时间: 2021-11-22

#include<iostream>
#include<vector>
#include<string.h>
using namespace std;

const int maxn = 15;  //最大数字不超过12位
long long dp[maxn][maxn][maxn]; //十进制数中长度为i,开头为j的数中k的个数
long long bin[maxn]; //i位中所有首位为i的数的个数
long long res[maxn]; //记录0~9个数
int d[maxn];
 
void init(){  //递推计算出每个整数
    bin[1] = 1; //1位首位为1只有1个
    for(int i = 2; i <= 13; i++) //后续都是前一个的10倍
        bin[i] = bin[i - 1] * 10;
    for(int i = 0; i <= 9; i++) //长度为1,以i开头中i的个数都是1
        dp[1][i][i]=1;
    for(int i = 2; i <= 13; i++)
        for(int j = 0; j <= 9; j++)
            for(int z = 0; z <= 9; z++){
                for(int k = 0; k <= 9; k++)
                    dp[i][j][z] += dp[i - 1][k][z]; //累加前面的
                dp[i][z][z] += bin[i - 1];
            }
}
 
void solve(long long x,int flag){ //计算1到x的所有数码出现次数
    int dnum = 0;  //记录当前数的位数
    long long y = x;
    memset(d, 0, sizeof(d));
    while(y){  //连除法计算当前x的位数
        d[++dnum] = y % 10; 
        y /= 10;
    }
    for(int i = 1; i <= dnum - 1; i++)//先计算位数小于当前数的位数
        for(int j = 1; j <= 9; j++)
            for(int k = 0; k <= 9; k++)
                res[k] += (dp[i][j][k] * flag); //累加,flag为1加,-1为减
    int temp = dnum;
    while(temp){//再计算位数等于当前数的位数
        for(int i = 0; i < d[temp]; i++){
            if(!i && temp == dnum)
                continue;//不能重复计算
            for(int j = 0; j <= 9; j++)
                res[j] += (dp[temp][i][j] * flag);
        }
        res[d[temp]] += (x % bin[temp] + 1) * flag;
        temp--;
    }
}
 
int main(){   
    init();
    long long a, b;
    cin >> a >> b;
    solve(b, 1); //计算1到b的所有数码次数
    solve(a - 1, -1);  //减去1到a-1的所有数码出现次数
    for(int i = 0; i <= 9; i++) //输出
        cout << res[i] << " ";
    cout << endl;
    return 0;
}

C 解法, 执行用时: 3ms, 内存消耗: 300KB, 提交时间: 2022-06-22

#include <stdio.h>

long long calcForDigit(long long a, int k) {
    if (a == 0) {
        return 0;
    }
    
    long long x, current, suffix, divide = 1, totalCount = 0;
    while(1) {
        suffix = a % divide + 1;
        current = a / divide;
        x = current / 10;
        current %= 10;
        
        if (k == 0) {
            if (current > k) {
                totalCount += ((x - 1 + 1) * divide);
            } else if (current == k) {
                totalCount += ((x - 1) * divide + suffix);
            } else {
                totalCount += ((x - 1) * divide);
            }
        } else {
            if (current > k) {
                totalCount += ((x + 1) * divide);
            } else if (current == k) {
                totalCount += (x * divide + suffix);
            } else {
                totalCount += (x * divide);
            }
        }
        
        if (k == 0) {
            if (x <= 9) {
                break;
            }
        } else {
            if (x < k) {
                break;
            }
        }
        
        divide *= 10;
    }
    
    return totalCount;
}

long long *calcDigitCount(long long a) {
    long long *result = (long long *)malloc(sizeof(long long) * 10);
    memset(result, 0, sizeof(long long) * 10);
    
    for (int i = 0; i < 10; i++) {
        result[i] = calcForDigit(a, i);
    }
    
    return result;
}

int main() {
    long long a, b;
    scanf("%lld %lld", &a, &b);
    
    long long *digitA, *digitB;
    digitA = calcDigitCount(a-1);
    digitB = calcDigitCount(b);
    
    for (int i = 0; i < 9; i++) {
        printf("%lld ", digitB[i] - digitA[i]);
    }
    printf("%lld", digitB[9] - digitA[9]);
    
    free(digitA);
    free(digitB);
    
    return 0;
}




C 解法, 执行用时: 3ms, 内存消耗: 360KB, 提交时间: 2022-05-08

#include <stdio.h>
#include <math.h>
int lengthOf(const char s[]) {
    int i = 0;
    while (s[i] != '\0')
        ++i;
    return i;
}
long long parseLongLong(const char s[]) {
    long long z = 0;
    int i;
    for (i = 0; s[i] != '\0'; ++i)
        z = z * 10 + s[i] - '0';
    return z;
}
void ll2Str(char dst[], int arr_size, long long num) {
    int i = -1, j;
    while (num > 0) {
        dst[++i] = num % 10 + '0';
        num /= 10;
    }
    if (i == -1)
        dst[++i] = '0';
    for (j = i / 2; j >= 0; --j) {
        char t = dst[j];
        dst[j] = dst[i - j];
        dst[i - j] = t;
    }
    for (++i; i < arr_size; ++i)
        dst[i] = '\0';
}
long long countDigit(char s[], int len, int i, int d) {
    int curr = s[i] - '0', k = len;
    long long z;
    if (s[i + 1] == '\0')
        return curr < d ? 0 : 1;
    if (s[i] == '0')
        return countDigit(s, len - 1, i + 1, d);
    z = (long long) pow(10, k - 2) * curr * (k - 1) + countDigit(s, len - 1, i + 1,
            d);
    if (curr > d)
        z += (long long) pow(10, k - 1);
    else if (curr == d)
        z += parseLongLong(s + i + 1) + 1;
    return z;
}
long long countAll(char s[], int k) {
    int i;
    long long z = (parseLongLong(s) - (long long) pow(10, k - 1) + 1) * k, m = 9;
    for (i = 1; i < k; ++i) {
        z += m * i;
        m *= 10;
    }
    return z;
}
int main() {
    char a[15], b[15];
    scanf("%s %s", a, b);
    int len_a = lengthOf(a), len_b = lengthOf(b);
    ll2Str(a, 15, parseLongLong(a) - 1);
    long long z[10];
    z[0] = countAll(b, len_b) - countAll(a, len_a);
    for (int i = 1; i < 10; ++i) {
        z[i] = countDigit(b, len_b, 0, i) - countDigit(a, len_a, 0, i);
        z[0] -= z[i];
    }
    printf("%lld", z[0]);
    for (int i = 1; i < 10; ++i)
        printf(" %lld", z[i]);
    return 0;
}

上一题