Mistrz Programowania

Mistrz Programowania 2025 runda 4 poziom d
Tagi: programowanie dynamiczne kombinatoryka teoria gier
Autor zadania: Mateusz Wesołowski
Treść

Omówienie zadania Gambit wieży

Strategia wygrywająca

Niech $x$ oznacza liczbę pełnych pięter (na których są $3$ klocki, możemy wyciągnąć dowolny z tych trzech klocków), a $y$ - liczbę pięter, z których możemy wyciągnąć tylko jeden klocek (pojedyńczych pięter).

Stosując indukcję matematyczną po możliwych pozycjach wykażemy, że pozycje $x,y$ parzyste są przegrywające, a pozostałe są wygrywające.

Baza indukcji: pozycja $x=y=0$ jest przegrywająca, bo nie możemy wykonać żadnego ruchu.

Krok indukcyjny Najpierw przeprowadzamy indukcję po możliwych wartościach $y$ dla danego $x$, potem zwiększamy $x$, w ten sposób przechodzimy indukcją po wszystkich interesujących nas parach $x,y$. Rozważmy parę $x_1,y_1$ i załóżmy, że dla każdej pary $x_1>x$ albo $y_1>y$ (jeśli $x=x_1$) przegrywamy wtedy i tylko wtedy, gdy $x$ i $y$ są parzyste, a pozostałe pozycje są wygrywające.

Rozważmy kilka przypadków:

Liczenie możliwych notacji

Użyjemy w tym celu programowania dynamicznego. Niech $dp[x][y]$ oznacza liczbę sposobów na otrzymanie pozycji $x,y$ modulo $10^9 + 7$. Dla początkowej pozycji $x,y$ $dp[x][y] = 1$. Podobnie jak w powyższym dowodzie, najpierw będziemy się iterować po coraz mniejszych $y$ dla danego $x$, a potem po coraz mniejszych $x$.

Policzenie rodzajów pięter pozwala nam na wyznaczenie zwycięzcy w czasie $O(1)$ i liczby możliwych notacji w $O(n^2)$, co dawało maksymalną liczbę punktów.

Przykładowe implementacje

C++

#include <bits/stdc++.h>
#define ll long long
using namespace std;

constexpr ll M = 1e9+7;
constexpr ll N = 5e3+7;
constexpr int debug = 0;

inline ll mod(ll a){
    return ((a%M)+M)%M;
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n; cin >> n;
    int p = 0, np = 0;  
    for(int i = 0; i < n; i++){
        string ok;
        for(int j = 0; j < 3; j++){
            string a; cin >> a;
            ok += a;
            if(j < 2) ok += " ";
        }
        //if(debug) cout << ok << '\n';
        if(i == 0) continue;
        if(ok == "1 1 0" || ok == "0 1 1") np++;
        else if(ok == "1 1 1") p++;
    }
    ll dp[p+3][np+p+3];
    for(int i = 0; i < p+3; i++){
        for(int j = 0; j < np+p+3; j++) dp[i][j] = 0;
    }
    (((p&1) == (np&1)) && ((np&1) == 0)) ? cout << "B\n" : cout << "A\n";
    dp[p][np] = 1;
    for(ll i = p; i >= 0; i--){
        for(ll j = np+p; j >= 0; j--){
            int a = (i&1), b = (j&1);
            if(a == b){       
                if(a){
                    dp[i-1][j+1] = mod(dp[i-1][j+1] + dp[i][j]*2*i);
                }
                else{
                    if(i > 0){
                        dp[i-1][j] = mod(dp[i-1][j] + dp[i][j]*i);
                        dp[i-1][j+1] = mod(dp[i-1][j+1] + dp[i][j]*2*i);
                    }
                    if(j > 0){
                        dp[i][j-1] = mod(dp[i][j-1]+dp[i][j]*j);
                    }

                }
            }
            else{
                if(a){
                    dp[i-1][j] = mod(dp[i-1][j] + dp[i][j]*i);
                }
                else{
                    dp[i][j-1] = mod(dp[i][j-1] + dp[i][j]*j);
                }
            }  

            //if(debug) cout << "DEBUG " << i << ' ' << j << ' ' << dp[i][j] << '\n';
        }
    }
    cout << dp[0][0] << '\n';
    
    return 0;
}

Python