Codeforces Round #235 (Div. 2) D Roman and Numbers(状态DP)

来源:互联网 发布:中昌数据最新消息 编辑:程序博客网 时间:2024/05/09 05:45

Roman is a young mathematician, very famous in Uzhland. Unfortunately, Sereja doesn't think so. To make Sereja change his mind, Roman is ready to solve any mathematical problem. After some thought, Sereja asked Roma to find, how many numbers are close to number n, modulo m.

Number x is considered close to number n modulo m, if:

  • it can be obtained by rearranging the digits of number n,
  • it doesn't have any leading zeroes,
  • the remainder after dividing number x by m equals 0.

Roman is a good mathematician, but the number of such numbers is too huge for him. So he asks you to help him.

Input

The first line contains two integers: n (1 ≤ n < 1018) and m (1 ≤ m ≤ 100).

Output

In a single line print a single integer — the number of numbers close to number n modulo m.

  • 数位DP,用dp[mask][m]记录余数为m的个数,mask标记使用了哪些位。
  • mask相当于做了一个hash。
   eg: 数1234
         1         -> 1
       10         -> 2
       11         -> 21  、 21
      100         ->3
      101        ->31  、13
      110        ->32   、23
      111        ->321  、 312 、 213 , 231 , 123 ,132  也就是相应位置3个数的组合
     1000      ->4
     1001      ->41   ,14  也就是相应2个数位的组合 
    111  这个状态3个数字都出现;  可以有“32” +“1” , “31” +“2”,......
                         就是由2个数字尾巴加1个数字转移得来。也就是说从下往上跟新状态。 
  • 避免0开头,将非0的单个数字的对应为赋值为1。
  • 将数字从大到小排序,每次添加一个状态相当于在任意数字组合的末尾添加一个数字dp[mask & (1 << i)][(m * 10 + digit[i]) % m] += dp[mask][m]。
  • 结果为dp[(1 << digitNum) - 1][0]除以排列数。
typedef long long LL ;LL dp[1<<18][101] ;LL num[18] ;LL repeat_dig[10] ;LL fac[19] ;LL DP(string str , int Mod){   int i , j , n , k ;   LL repeat = 1 ;   memset(repeat_dig , 0 , sizeof(repeat_dig)) ;   memset(dp , 0 , sizeof(dp)) ;   n = str.length() ;   for(i = 0 ; i < n ; i++){       num[i] = str[i] - '0' ;       repeat_dig[num[i]]++ ;   }   for(i = 0 ; i <= 9 ; i++)       repeat *= fac[repeat_dig[i]] ;   sort(num , num + n) ;   for(i = 0 ; i < n ; i++){      if(num[i] > 0)         dp[1<<i][num[i]%Mod] = 1 ;   }   for(i = 1 ; i < (1<<n) ; i++){     for(j = 0 ; j < Mod ; j++){         if(dp[i][j]){             for(k = 0 ; k < n ; k++){                 if((i & (1<<k)) == 0)                    dp[i|(1<<k)][(j*10 + num[k]) % Mod] += dp[i][j] ;             }         }     }   }   return dp[(1<<n)-1][0]/repeat ;}int main(){   fac[0] = 1 ;   for(int i = 1 ; i <= 18 ; i++)      fac[i] = fac[i-1] * i ;   string s ;   int mod ;   while(cin>>s>>mod){        cout<<DP(s, mod)<<endl ;   }   return 0 ;}



记忆化搜索(好懂):
typedef long long LL ;LL dp[1<<18][101] ;LL num[18] ;LL repeat_dig[10] ;LL fac[19] ;bool visited[1<<18] ;int n , Mod ;void dfs(int state){   if(visited[state])      return ;   visited[state] = 1 ;   for(int i = 0 ; i < n ; i++){       if((state | (1<<i)) != state)          continue ;       int now = state - (1<<i) ;       if(now == 0)          continue ;       dfs(now) ;       for(int j = 0 ; j < Mod ; j++)         dp[state][(j*10 + num[i]) % Mod]  += dp[now][j] ;   }}LL DP(string str){   int i , j  , k ;   LL repeat = 1 ;   memset(repeat_dig , 0 , sizeof(repeat_dig)) ;   memset(dp , 0 , sizeof(dp)) ;   memset(visited , 0 , sizeof(visited)) ;   n = str.length() ;   for(i = 0 ; i < n ; i++){       num[i] = str[i] - '0' ;       repeat_dig[num[i]]++ ;   }   for(i = 0 ; i <= 9 ; i++)       repeat *= fac[repeat_dig[i]] ;   for(i = 0 ; i < n ; i++){       if(num[i])           dp[1<<i][num[i]%Mod] = 1 ;   }   dfs((1<<n)-1) ;   return dp[(1<<n) -1][0]/repeat ;}int main(){   fac[0] = 1 ;   for(int i = 1 ; i <= 18 ; i++)      fac[i] = fac[i-1] * i ;   string s ;   while(cin>>s>>Mod){        cout<<DP(s)<<endl ;   }   return 0 ;}


0 0
原创粉丝点击