【codeforces】55D. Beautiful numbers 数位DP

传送门:【codeforces】55D. Beautiful numbers


题目分析:被每一位整除则用二进制记录已经包括的数字的个数,以及对2520取模后的状态。由于对5整除当且仅当最后一个数为0或5,对2整除当且仅当最后一个数为偶数,且1~9的最小公倍数为2520,不包括2,5后的最小公倍数为252,所以除最后一层对2520取模,其余时候都对252取模即可。由于整除的状态有限,最多只有48个,于是我们预处理出这48个数,并来一个映射就好(不然会TLE)。


通过这题我才知道dp只用一次清零即可。。。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

LL L , R ;
LL dp[19][252][48] ;
int a[2521] , cnt ;
int digit[19] ;

int gcd ( int a , int b ) {
	return b ? gcd ( b , a % b ) : a ;
}

LL dfs ( int cur , int limit , int x , int y , LL ans = 0 ) {
	if ( cur < 0 ) return x % y == 0 ;
	if ( ~dp[cur][x][a[y]] && !limit ) return dp[cur][x][a[y]] ;
	int n = limit ? digit[cur] : 9 ;
	For ( i , 0 , n ) ans += dfs ( cur - 1 , limit && i == digit[cur] , cur ? ( x * 10 + i ) % 252 : ( x * 10 + i ) , i ? y * i / gcd ( y , i ) : y ) ; 
	return limit ? ans : dp[cur][x][a[y]] = ans ;
}

LL solve ( LL n ) {
	int n1 = 0 ;
	while ( n ) {
		digit[n1 ++] = n % 10 ;
		n /= 10 ;
	}
	return dfs ( n1 - 1 , 1 , 0 , 1 ) ;
}

int main () {
	int T ;
	cnt = 0 ;
	For ( i , 1 , 2520 ) if ( 2520 % i == 0 ) a[i] = cnt ++ ;
	clr ( dp , -1 ) ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) {
		scanf ( "%I64d%I64d" , &L , &R ) ;
		printf ( "%I64d\n" , solve ( R ) - solve ( L - 1 ) ) ;
	}
	return 0 ;
}


你可能感兴趣的:(HDU)