mayoko’s diary

プロコンとかいろいろ。

SRM 644 div1 med:MakingTournament

やっとわかった…多分3日くらい考えてた。

問題:N人がK回戦のトーナメントをする。1回の試合は2人以上なら何人参加しても良いが,勝者は1人のみである。K回勝ち抜く人は何人いても良い。このようなトーナメントの作り方は何通りあるか。

解法:状態として,「i回戦を勝ち抜いた人がいるかいないか(i=0〜K)」というものを考える。例えば,1回戦を勝ち抜いた人と3回戦を勝ち抜いた人がいる場合,状態は001010(右から1番目と3番目のビットが立っている)とかなります。
で,次のように考えることによって,行列の累乗の計算(いわゆるオートマトン)に帰着させます。知らない人いたら蟻本のp180あたり読んだほうが良いですね。
・ある人を追加する時,その人がどれだけ勝つかに依存して,上で説明した状態を遷移させる

具体的に言うと,例えば今の状態が001011だとすると,次の人を参入させた時,
・1回戦で負ける
・2回戦で負ける
・2回戦まで勝ち抜く(そのあとは3回戦を勝った人がいないので考えない)
という3通りがあります。それぞれの場合において,状態の遷移は,
・001011->001011(1回戦に参加する人が増えたと考える。あとでこの人たちに勝つ人がやってくる)
・001011->001010(1回戦に参加していた人たちが皆負けて,今回考慮した人が2回戦に勝ち進んだ。あとで2回戦でこの人たちをやっつける人が来る)
・001011->001100(1,2回戦に参加していた人たちが皆負けて,現在2回戦に勝ち残った人,3回戦に勝ち残った人しかいない)
という状態遷移をします。注意が必要なのは,K回戦を勝ち抜いた場合の状態遷移ですが,これは
・111111->000000となります(実際にはオーバーフロー的な感じで111111->1000000となっているイメージ)。
ここまでイメージできれば後は簡単で,N人参入するのだから行列はN乗すればOKです。最終的な状態は参加者が負けるかK回勝ち抜けるかのどっちかになっていないといけないので,題意の状態は000000という感じになっています。
以下ソースコード。なんかめっちゃ遅いんだよなぁ…500msくらいかかった。

typedef vector<vector<ll> > matrix;
const ll MOD = 1e9+7;

matrix mul(const matrix& A, const matrix& B) {
    int n = A.size();
    matrix ret(n, vector<ll>(n));
    for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) {
        for (int k = 0; k < n; k++) {
            ret[i][j] += A[i][k] * B[k][j];
            ret[i][j] %= MOD;
        }
    }
    return ret;
}

matrix powMat(matrix A, ll p) {
    int n = A.size();
    matrix ret(n, vector<ll>(n));
    for (int i = 0; i < n; i++) ret[i][i] = 1;
    while (p) {
        if (p&1) {
            ret = mul(ret, A);
        }
        A = mul(A, A);
        p >>= 1;
    }
    return ret;
}

class MakingTournament {
public:
    int howManyWays(long long N, int K) {
        int kk = 1<<(K+1);
        matrix Mat(kk, vector<ll>(kk));
        for (int i = 0; i < (1<<K); i++) {
            for (int j = 0; j <= K; j++) {
                if ((i&((1<<j)-1)) == (1<<j)-1) {
                    int x = (i^((1<<j)-1)) | 1<<(j);
                    if (j == K) x = 0;
                    Mat[x][i] = 1;
                }
            }
        }
        matrix Mat2 = powMat(Mat, N);
        return Mat2[0][0]%MOD;
    }
};