P2602 [ZJOI2010]数字计数

偷得题面

设f[i]代表第i位上不算前导零每个数出现多少次

比如54321

首先是5,,5在万位上,那么个十百千位每个数都会出现至少5*f[bit]次,然后万位上,5以下的数都会出现,然后以此类推

最后加完所有位之后,在处理4321这样的跑不满的零位就好了qwq

代码

DFS版

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#define int long long
using namespace std ;
int read() {
	int x = 0 , f = 1 ; char s = getchar() ;
	while(s > '9' || s < '0') {if(s == '-') f = -1 ; s = getchar() ;}
	while(s <='9' && s >='0') {x = x * 10 + (s-'0'); s = getchar() ;}
	return x*f ;
}
int l , r ;
int f[15][15] , num[15]  ;
int dfs(int pos,int sum ,int lead ,int limit,int d) {
	if(!pos) return sum ;
	if(!limit&&f[pos][sum]!=-1&&!lead) return f[pos][sum] ;
	int up = limit? num[pos] : 9 ;
	int ans = 0 ;
	for(int i = 0 ; i <= up ; i ++) {
		ans += dfs(pos-1,sum+((i||!lead)&&(i==d)),lead&&(i==0) , limit && (i == num[pos]),d) ;
 	}
 	if(!lead && !limit) f[pos][sum] = ans ;
 	return ans ;
}
int calc(int x,int d) {
	int len = 0 ;
	while(x) {
		num[++len] = x % 10 ;
		x /= 10 ;
	}
	memset(f,-1,sizeof f) ;
	return dfs(len,0,1,1,d) ;
}
signed main () {
	l = read() , r = read() ;
	for(int i = 0 ; i < 10 ; i ++) {
		cout << calc(r,i) - calc(l-1,i) << " " ;
	}
	return 0 ;
}

循环

#include <bits/stdc++.h>
#define int long long
using namespace std ;
int a , b ;
int ten[15] , f[15] ;
int cnta[15] , cntb[15] , num[10] ;
void work(int x ,int cnt[])  {
	for(int i = 0 ; i < 10 ; i ++) num[i] = 0 ;
	int len = 0 ;
	while(x) {//拆数 
		num[++len] = x%10 ;
		x = x / 10 ; 
	}
	for(int i = len ; i >= 1 ; i --) {
		for(int j = 0 ; j <= 9 ; j ++) {
			cnt[j] += f[i-1] * num[i] ; //第i位之后的每位每个数数都会出现i次 
		}
		for(int j = 0 ; j < num[i] ; j ++) {
			cnt[j] += ten[i-1] ;//第i位是x,那么在x之前的都会在出现10^(bit-1)次 
		}
		//--------以上只是处理A000000....00这亚子----------//
		//--------以下处理零位--------// 
		int num2 = 0 ;
		for(int j = i-1 ; j >= 1 ; j --) {
			num2 = num2 * 10 + num[j] ;//加上零位 
		}
		cnt[num[i]] += num2 + 1 ;// num2是零位,1是最高位
		cnt[0] -= ten[i-1] ;//清除前导零 
	}
}  
signed main () {
	scanf("%lld%lld",&a,&b) ;
	ten[0] = 1 ;//10的阶乘,每一位代表多少 
	for(int i = 1 ; i <= 15 ; i ++) {
		f[i] = f[i-1] * 10 + ten[i-1] ;//在第i位有数字的时候,每个数码有多少个 
		ten[i] = 10*ten[i-1] ; 
	}
	work(a-1,cnta) ;//work(0-x的各数位,统计答案的数组) 
	work(b,cntb) ;
	for(int i = 0 ; i <= 9 ; i ++) printf("%lld ",cntb[i] - cnta[i]) ;
	return 0 ;
} 

举个栗子: 32345

首先 是 5f[0] ,然后4f[1] (也就是4个一位数所有的数,因此当前位数4是没有算上的),然后是3*f[2] (也就是4个两位数所有的数,因此当前位数3是没有算上的)....以此类推

然后我们成功的处理完len-1位(y因为这些位数一定是合法的)后,开始考虑len位,首先,num[len]-1个ten[i-1]次(y因为最后一位不算零头也只能出现这些),然后在填上零头就好了

原文地址:https://www.cnblogs.com/lyt020321/p/11750513.html