谜一般的数位DP…
BZOJ1799传送门
给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。(就喜欢这种短小精悍的题面=w=)
a,b都在long long范围内
这个数位DP不能像普通的那样做。
按照平常的思维,会定义dp[i][j][k]为当前扫到前i位,数位和为j,数字取模数位和后为k,跑一遍R的dp再跑一遍L-1的dp
然而跑一遍是不行的…因为前面的取模和后面的取模没有关系,之间是不能转移的….
所以这道题需要枚举数位和(也就是模数),对每个数位和都跑一次dfs,因为数字在long long范围内,数位和最大只会有9*18=162,是可过的。
#include
#include
#include
using namespace std ;
long long L , R , dp[20][165][165] ;
int w[20] , cnt ;
void div( long long x ){
cnt = 1 ;
while( x ){
w[cnt] = x%10 ;
x /= 10 ; cnt ++ ;
} cnt -- ;
}
long long dfs( int dep , int sum , int am , int mmod , bool limit ){
if( dep == 0 ) return am == 0 && sum == mmod ;
if( sum > mmod ) return 0 ;
if( !limit && dp[dep][sum][am] != -1 ) return dp[dep][sum][am] ;
int lim = ( limit ? w[dep] : 9 ) ;
long long rt = 0 ;
for( int i = 0 ; i <= lim ; i ++ )
rt += dfs( dep - 1 , sum + i , ( am * 10 + i )%mmod , mmod , limit && i == lim ) ;
if( !limit ) dp[dep][sum][am] = rt ;
return rt ;
}
void solve(){
long long ans = 0 ;
div( R ) ;
for( int i = 1 ; i <= 162 ; i ++ ){
//printf( "now :: %d\n" , i ) ;
memset( dp , -1 , sizeof( dp ) ) ;
ans += dfs( cnt , 0 , 0 , i , true ) ;
}
div( L - 1 ) ;
for( int i = 1 ; i <= 162 ; i ++ ){
memset( dp , -1 , sizeof( dp ) ) ;
ans -= dfs( cnt , 0 , 0 , i , true ) ;
}
printf( "%lld" , ans ) ;
}
int main(){
scanf( "%lld%lld" , &L , &R ) ;
solve() ;
}