SRM 391 500pt: KeysInBoxes
問題概要
1~N(<20)の箱がある。それぞれの箱を開ける鍵が1個ずつある。鍵を箱の中に1個ずつ適当に入れて全ての箱の鍵を占める。あなたはM(
考えたこと
practiceで通ったコード
計算量O(N^3)くらい?
#include <string> #include <sstream> #include <algorithm> using namespace std; typedef long long int64; const int MAX_N = 20; int64 fact[MAX_N+1]; int64 comb[MAX_N+1][MAX_N+1]; int64 memo[MAX_N+1][MAX_N+1]; bool visited[MAX_N+1][MAX_N+1]; struct KeysInBoxes { string getAllKeys(int N, int M) { //階乗の計算 fact[0] = 1; for(int i=1; i<=MAX_N; i++){ fact[i] = i * fact[i-1]; } //二項係数の計算 for(int n=0; n<=N; n++){ for(int k=0; k<=n; k++){ comb[n][k] = fact[n] / (fact[k] * fact[n-k]); } } int64 cnt = 0, total = fact[N]; //k個の巡回置換に分解する方法 for(int k=1; k<=M; k++){ cnt += func(N, k); } //約分 int64 d = __gcd(cnt, total); cnt /= d; total /= d; //文字列へ変換 stringstream ss; ss << cnt << '/' << total; return ss.str(); } int64 func(int N, int k){ //メモ化処理 if(visited[N][k]){ return memo[N][k]; } visited[N][k] = true; int64& ret = memo[N][k]; //基底 if(k == 1){ return ret = fact[N-1]; } //ある特定の一個がサイズいくつの巡回置換に含まれるか for(int sz=1; sz<=N; sz++)if(N - sz >= k - 1){ ret += comb[N-1][sz-1] * fact[sz-1] * func(N-sz, k-1); } return ret; } };