土下座しながら探索中

主に競技プログラミング

Typical DP Contest D : サイコロ

問題リンク : D: サイコロ - Typical DP Contest | AtCoder

解法 :
Dを素因数分解したとき、その素因数に2、3、5以外のものが存在するのであれば求める確率は0となる
(サイコロの目が1から6までしかないので、例えば7は2、3、5の積ではどうやっても作れない)
それ以外の場合については、次のようなdp配列を用意し解を求める
dp[i][j][k][l] := i回さいころを振ったとき、素因数として2がj個、3がk個、5がl個となるような確率
また、愚直に配列を確保できないため、i,j,kがそれぞれ対応する素因数の数以上になったのであれば、その素因数の数にまとめる

コード :

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-10)
#define equals(a,b) (fabs((a)-(b))<EPS)

using namespace std;

typedef long long ll;
const int MAX = 75;
double dp[101][MAX][MAX][MAX];
//                      2              3              5 
int N,factor[3][6] = {{0,1,0,2,0,1},{0,0,1,0,0,1},{0,0,0,0,1,0}};//1,2,3,4,5,6
ll D;

map<ll,int> prime_factor(ll n) {
  map<ll,int> res;
  for(ll i=2;i*i<=n;i++) {
    while(n % i == 0) {
      ++res[i];
      n /= i;
    }
  }
  if(n != 1)res[n] = 1;
  return res;
}

int main() {
  cin >> N >> D;
  int a_2 = 0, a_3 = 0, a_5 = 0;
  map<ll,int> pf = prime_factor(D);
  for(map<ll,int>::iterator it=pf.begin();it!=pf.end();it++) {
    if( it->first != 2LL && it->first != 3LL && it->first != 5LL ) {
      puts("0.0000000000");
      return 0;
    }
    if( it->first == 2LL ) a_2 = it->second;
    if( it->first == 3LL ) a_3 = it->second;
    if( it->first == 5LL ) a_5 = it->second;
  }

  dp[0][0][0][0] = 1;
  rep(i,N) rep(_2,a_2+1) rep(_3,a_3+1) rep(_5,a_5+1) if( !equals(dp[i][_2][_3][_5],0) ) {
    rep(dice,6) {
      int n_2 = _2 + factor[0][dice], n_3 = _3 + factor[1][dice], n_5 = _5 + factor[2][dice];
      dp[i+1][min(a_2,n_2)][min(a_3,n_3)][min(a_5,n_5)] += dp[i][_2][_3][_5] / 6.0;
    }
  }

  printf("%.10f\n",dp[N][a_2][a_3][a_5]);
  return 0;
}