[Test 2016-5-19]

数字
【问题描述】
我们定义一个正整数的位积等于各个位数字的乘积,例如2612的位积是2×6×1×2=24。
  定义一个数的自积该数本身与该数位积的乘积。例如,2612的自积等于2612×24=62688
  给你两个正整数A和B,计算有多少个数的自积在A到B之间。
【输入格式】
第一行包含两个整数A和B(1<=A<=B<10^18)。
【输出格式】
输出有多少个数的自积在A到B之间。
【样例输入1】20 30
【样例输出1】2
【样例输入2】145 192
【样例输出2】4
【样例输入3】2224222 2224222
【样例输出3】1
【样例说明】样例2中19、24、32和41的自积分别为171、192、192和164。

【数据范围】25%的数据,A,B<=10^8 40%的数据,A,B<=10^12

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;

ll A, B, Nw, ret, fac[20];
int num[20], lim[20], w[20];

int cnt;

void FJ(ll x){
	cnt = 0;
	while(x){
		lim[++ cnt] = x % 10;
		x /= 10;
	}
}

ll check(ll x){
	ll ret = 0, n = Nw / x;
	int len = 0;
	if(n == 0)return 0;
	for(int i = 1; i < 10; i ++)len += num[i];
	for(int i = 1; i < 10; i ++)w[i] = num[i];
	if(len == 0)return 0;
	FJ(n);
	if(cnt < len)return 0;
	if(len < cnt){
		ret = fac[len];
		for(int i = 1; i < 10; i ++)
		    ret /= fac[num[i]];
		return ret;
	}
	
	for(int i = len; i > 0; i --){
		for(int j = 1; j < lim[i]; j ++){
			if(w[j]){
				w[j] --;
				ll c = fac[i - 1];
				for(int k = 1; k <= 9; k ++)
				    c /= fac[w[k]];
				w[j] ++;
				ret += c;
			}
		}
		if(!w[lim[i]])return ret;
		-- w[lim[i]];
	}
	return ret + 1;
}

void dfs(int dep, ll SUM, ll MUL){
	if((double)MUL * SUM > Nw)return;
	if(dep == 10){ret += check(MUL); return;}
	for(int i = 0; (double)SUM * MUL <= Nw; i ++){
		num[dep] = i;
		dfs(dep + 1, SUM, MUL);
		SUM = SUM * 10 + dep;
		MUL = MUL * dep;
	}
	num[dep] = 0;
}

ll solve(ll x){
	Nw = x;
	ret = 0;
	dfs(1, 0, 1);
	return ret;
}

int main(){
	freopen("num.in", "r", stdin);
	freopen("num.out", "w", stdout);
	fac[0] = 1;
	for(int i = 1; i < 20; i ++)fac[i] = fac[i-1] * i;
	cin >> A >> B;
	cout << solve(B) - solve(A - 1) << endl;
	return 0;
}

  

 

posted @ 2016-05-19 18:43  _Horizon  阅读(140)  评论(0)    收藏  举报