• 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
    ", prod);
      return 0;
    }
    
  • 相关阅读:
    linux初学者-普通磁盘分区篇
    linux初学者-延迟及定时任务篇
    linux初学者-网络管理篇
    linux初学者-虚拟机联网篇
    贪吃蛇Ground Java实现(二)
    贪吃蛇 Java实现(一)
    创建异常
    IO流
    实训第一天
    Array 遍历数组
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-loj2731.html
Copyright © 2020-2023  润新知