haraduka's diary

やる気が欲しい

ARC025 D問題 25個の整数

ちょっとずつ残ってるD問題を潰していきますね。

1~25の数字が5x5のマスに並んでいて(0で少し隠れている)、それから連続する3つをとってきたときに昇順または降順にならないように0で隠れているところを埋める方法が何通りあるかを計算する問題
難しい…

でも気づいてしまえば簡単。
普通に穴を埋めていくだけでN!の時間取られてしまうので、どうにかして他の方法を探す。
なるべく規則性が得られる方向へ…
1~25の数字を順に入れていったらどうか、そうすればslideにあるように、Cのケースにならないようにどんどんマスを埋めていけば良い。
さらに、すでに置いた箇所をまとめて管理することもできる。
つまり今回はint dp[1<<25];のような配列を作り、それぞれのbitが、その場所に数字が入っているかを表すようにしてbitDPすれ良い。

正直スライドを見ればだいたい理解できる気がする。まぁいいや。

constexpr ll mod = (ll)1e9+7;
int dp[1 << 25];

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    vector<int> v;
    int fix[26];
    memset(fix, -1, sizeof(fix));
    rep(i, 25){
        int n; cin >> n;
        if(n == 0){
            v.push_back(i);
        }else{
            fix[n] = i;
        }
    }
    memset(dp, 0, sizeof(dp));
    dp[0] = 1;
    for(int i=0; i<(1<<25)-1; i++){
        if(dp[i]){
            int c = __builtin_popcount(i) + 1;
            int r = fix[c];
            if(r != -1){
                int y = r/5, x = r%5; //x,yにcを入れる
                if((i&(1<<r)) == 0){
                    if(y > 0 && y < 4 && ((i>>(r-5))^(i>>(r+5)))&1) continue; //slideのCのケース
                    if(x > 0 && x < 4 && ((i>>(r-1))^(i>>(r+1)))&1) continue; //slideのCのケース
                    dp[i|(1<<r)] += dp[i];
                    if(dp[i|(1<<r)] >= mod) dp[i|(1<<r)] -= mod;
                }
            }else{
                for(int j : v){
                    int y = j/5, x = j%5;
                    if((i&(1<<j)) == 0){
                        if(y > 0 && y < 4 && ((i>>(j-5))^(i>>(j+5)))&1) continue; //slideのCのケース
                        if(x > 0 && x < 4 && ((i>>(j-1))^(i>>(j+1)))&1) continue; //slideのCのケース
                        dp[i|(1<<j)] += dp[i];
                        if(dp[i|(1<<j)] >= mod) dp[i|(1<<j)] -= mod;
                    }
                }
            }
        }
    }
    cout << dp[(1<<25)-1] << endl;
}