1. 程式人生 > 其它 >JOISC 2016 Day 1 棋盤遊戲

JOISC 2016 Day 1 棋盤遊戲

題意

JOI 君有一個棋盤,棋盤上有 \(N\)\(3\) 列 的格子。JOI 君有若干棋子,並想用它們來玩一個遊戲。初始狀態棋盤上至少有一個棋子,也至少有一個空位。

遊戲的目標是:在還沒有放棋子的格子上依次放棋子,並填滿整個棋盤。在某個格子上放置棋子必須滿足以下條件之一:

  1. 這個格子的上下一格都放有棋子;
  2. 這個格子的左右一格都放有棋子。

JOI 君想知道有多少種從初始狀態開始,並達到遊戲目標的方案,這個答案可能會非常大。請你幫 JOI 君算出這個答案,並對 \(10^9+7\) 取模。

題解

注意到可以通過上下填的只有第二行。

先判斷是否有解。如果第一行或第三行存在兩個連續的空格,那麼無解,否則有解。那麼一三兩行可以在任意時刻填棋子。

對第二行的空格連續段分別計算,最後組合即可。

\(H(i, r), V(i, r)\) 分別表示考慮到當前連續段\(i\) 列,當前中間格在\(i\) 列的空格中\(r\) 個填,且是依賴左右/上下兩邊填的方案數。如果四個方向都有棋子,則填上下兩邊

設第 \(i\) 列中第一、三行共 \(t\) 個空位。

分三種情況考慮:

  • \(i, i - 1\) 兩列均為縱向,則相互不影響。

    \[V(i, r) \gets (r-1)^\underline{t}\sum_x V(i - 1, x) \]

    其中下降冪為 \(i\) 上下的方案數。

  • \(i\) 列為縱向,\(i-1\) 列為橫向,則 \(i\)

    列必須早於 \(i - 1\) 列填。

    \[V(i, r) \gets (r-1)^\underline{t} \sum_{x \ge i - t} H(i - 1, x) \]
  • \(i\) 列為橫向,\(i-1\) 列為縱向。

    此時需要保證填中間格是上下格不能已經全部有棋子,且左邊的中間格已經有棋子。

    為了避免討論相對順序,我們先只加入中間格,然後再加入對順序沒有影響的上下格。

    設上下中有 \(c\) 個在中間之前填:

    \[H(i, r + c) \gets \sum_{x<r} V(i - 1, x) \cdot E(r - 1, c) \cdot E(\mathrm{cnt} - r, t - c) \cdot t! \]

    其中 \(r + c\)

    為加入上下格後對 \(i\) 的排名的影響,\(\mathrm{cnt}\) 為當前連通塊中總空格數,\(E(i, j)\) 表示在 \(i\) 個元素之間的 \(i + 1\) 個空格中放置 \(j\) 個相同物品的方案數。

使用字首和,總時間複雜度 \(\mathcal O(n^2)\)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
using namespace std;

#define LOG(f...) fprintf(stderr, f)
#define all(cont) begin(cont), end(cont)
// #define DBG(f...) printf("L %4d: ", __LINE__), printf(f)
#define DBG(f...) void()

char getv() {
  char ch;
  while (isspace(ch = getchar()));
  return ch;
}

using ll = long long;

const int N = 2005;
const int M = 1000000007;

char a[N][3];

int dp[N][N * 3][2];
vector<int> fac, ifac;

int sub(int x, int y) { return x < y ? x - y + M : x - y; }
void inc(int &x, int y) { if ((x += y) >= M) x -= M; }
int power(int x, int y) {
  int p = 1;
  for (; y; y >>= 1, x = (ll)x * x % M)
    if (y & 1) p = (ll)p * x % M;
  return p;
}
int inv(int x) { return power(x, M - 2); }

void prefac(int n) {
  fac.resize(n + 1); ifac.resize(n + 1);
  fac[0] = 1;
  for (int i = 1; i <= n; ++i)
    fac[i] = (ll)fac[i - 1] * i % M;
  ifac[n] = inv(fac[n]);
  for (int i = n; i; --i)
    ifac[i - 1] = (ll)ifac[i] * i % M;
}

int binom(int n, int m) { return (ll)fac[n] * ifac[m] % M * ifac[n - m] % M; }
int ff(int n, int m) { return (ll)fac[n] * ifac[n - m] % M; }

int main() {
#ifndef ONLINE_JUDGE
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  int n;
  scanf("%d", &n);
  prefac(3 * n);
  for (int c = 0; c < 3; ++c)
    for (int i = 1; i <= n; ++i)
      a[i][c] = getv();
  for (int i = 1; i <= n; ++i)
    if ((a[i][0] == 'x' && (a[i - 1][0] != 'o' || a[i + 1][0] != 'o')) || 
        (a[i][2] == 'x' && (a[i - 1][2] != 'o' || a[i + 1][2] != 'o'))) {
      puts("0");
      return 0;
    }

  int empt = 0, prod = 1;
  int siz = 0;

  for (int i = 1; i <= n; ++i) {
    int t = int(a[i][0] == 'x') + int(a[i][2] == 'x');
    empt += t + int(a[i][1] == 'x');
    if (a[i][1] != 'x') // skip
      siz = 0;
    else if (a[i - 1][1] != 'x') { // init
      siz = t + 1;
      for (int j = t + 1; j <= siz; ++j)
        dp[i][j][0] = ff(j - 1, t);
      if (i != 1)
        for (int c = 1; c <= t; ++c)
          for (int j = t - c + 1; j <= siz - c; ++j)
            dp[i][j][1] = (dp[i][j][1] + (ll)binom(j - 1, t - c) * binom(siz - j, c) * fac[t]) % M;
    }
    else {
      int psum0 = dp[i - 1][siz][0], psum1 = dp[i - 1][siz][1];

      ++siz;
      for (int c = 0; c < t; ++c)
        for (int j = 1; j <= siz; ++j)
          dp[i][j + c][1] = (dp[i][j + c][1] + (ll)dp[i - 1][j - 1][0] * (c == 0 ? 1 : j) % M * binom(siz - j + t - c, t - c) * fac[t]) % M;

      siz += t;
      for (int j = t + 1; j <= siz; ++j)
        dp[i][j][0] = ((ll)psum0 * ff(j - 1, t) + (ll)sub(psum1, dp[i - 1][j - t - 1][1]) * ff(j - 1, t)) % M;
    }

    if (a[i][1] == 'x')
      for (int j = 2; j <= siz + 3; ++j)
        inc(dp[i][j][0], dp[i][j - 1][0]), inc(dp[i][j][1], dp[i][j - 1][1]);
    if (a[i + 1][1] != 'x' && a[i][1] == 'x') {
      int ways = dp[i][siz][0];
      if (i != n) inc(ways, dp[i][siz][1]);
      prod = (ll)prod * ways % M * ifac[siz] % M;
      siz = 0;
    }
  }

  prod = (ll)prod * fac[empt] % M;
  printf("%d\n", prod);
  return 0;
}