codeforce 55D Beautiful numbers

D. Beautiful numbers
time limit per test:4 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

Volodya 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 each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.
Input

The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers li and ri (1 ≤ li ≤ ri ≤ 9 · 1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).
Output

Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).
Sample test(s)
Input
1
1 9
Output
9
Input
1
12 15
Output

2


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int num[20], ln;
__int64 f[20][2521][50];
int hash[2521];

void swap(int &a, int &b){
	int t = a;
	a = b; 
	b = t;
}

int gcd(int a, int b){
	int c;
	while(b){
		c = a % b;
		a = b;
		b = c;
	}
	return a;
}

int Lcm(int a, int b){
//	printf("Lcm %d, %d\n", a, b);	
	if (a < b) swap(a, b);
	if (b == 0) return a;
	return a * b / gcd(a, b);
}

__int64 dfs(int i, int mod, int mark, bool flag){
	int p, high;
	__int64 ret;
	
//	printf("dfs(%d, %d, %d, %d)\n", i, mod, mark, flag);
	
	if (i <= 0) return (mod % mark == 0);
	if (!flag && ~f[i][mod][hash[mark]]) return f[i][mod][hash[mark]];
	
	high = flag ? num[i] : 9;
	ret = 0;
	for (p = 0; p <= high; p++){
//		printf("p = %d\n", p);
		ret += dfs(i - 1, (mod * 10 + p) % 2520, Lcm(mark, p), flag && (p == high));
	}
	if (!flag) f[i][mod][hash[mark]] = ret;
	return ret;
}


void trans(__int64 n){
	ln = 0;
	while(n){
		num[++ln] = n % 10;
		n = n / 10;
	}
}

__int64 calc(__int64 n){
	trans(n);
	return dfs(ln, 0, 1, true);
}

void init(){
	int i, j, k, l, cnt = 0;
	memset(f, 0xff, sizeof(f));
	hash[0] = 0;
	for (i = 1; i < 10; i *= 2)
		for (j = 1; j < 10; j *= 3)
			for (k = 1; k < 10; k *= 5)
				for (l = 1; l < 10; l *= 7){
					hash[i * j * k * l] = ++cnt;
				}
}

int main(){
	__int64 L, R;
	int T;
	init();
	scanf("%d", &T);
	while(T--){
		scanf("%I64d %I64d", &L, &R);
		printf("%I64d\n", calc(R) - calc(L - 1));
	}
	return 0;
}

/**************************
按位dp
表示看解題報告後寫的。。見http://hi.baidu.com/%B1%BFС%BA%A2_shw/blog/item/a30e5c230dffdfe7d6cae200.html
開始時想不通怎麼判是否整除所有位的數,想存模2..9的所有餘數的情況。。。
其實根本沒必要,我們希望知道一個數n是否整除LCM{n的每位數}(記做mark),而LCM{2...9} = 2520的,一定能整除mark,
所以 mark | n <=> mark | (n % 2520)
或者說 n % mark = (k * 2520 + k1) % mark = k1 % mark 
於是記錄下模2520和當前lcm(程序中爲mark),判斷下模2520餘出來的那部分能否整除當前lcm即可
爲方便存儲,mark一共只有4 * 3 * 2 * 2 = 48種(分別代表2,3,5,7在小於10可能取到的冪數),hash一下節省空間

話說這樣的空間消耗,真怕會爆掉的。。。但在codeforce裏慢慢地跑了300ms+過了Accepted。。。
還有更進一步優化,詳見http://codeforces.ru/blog/entry/1109?locale=en
**************************/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章