結果

問題 No.1397 Analog Graffiti
ユーザー 👑 emthrmemthrm
提出日時 2021-02-19 18:43:48
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 9,747 ms / 10,000 ms
コード長 9,423 bytes
コンパイル時間 3,865 ms
コンパイル使用メモリ 260,036 KB
実行使用メモリ 6,944 KB
最終ジャッジ日時 2024-07-01 12:25:34
合計ジャッジ時間 77,681 ms
ジャッジサーバーID
(参考情報)
judge4 / judge5
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
6,816 KB
testcase_01 AC 2 ms
6,940 KB
testcase_02 AC 9,747 ms
6,940 KB
testcase_03 AC 2 ms
6,940 KB
testcase_04 AC 2 ms
6,940 KB
testcase_05 AC 9,742 ms
6,940 KB
testcase_06 AC 2 ms
6,940 KB
testcase_07 AC 2 ms
6,940 KB
testcase_08 AC 2 ms
6,940 KB
testcase_09 AC 2 ms
6,944 KB
testcase_10 AC 1 ms
6,944 KB
testcase_11 AC 9,567 ms
6,944 KB
testcase_12 AC 1,542 ms
6,940 KB
testcase_13 AC 9,276 ms
6,940 KB
testcase_14 AC 9,365 ms
6,940 KB
testcase_15 AC 31 ms
6,944 KB
testcase_16 AC 1,443 ms
6,944 KB
testcase_17 AC 8,744 ms
6,940 KB
testcase_18 AC 8,959 ms
6,944 KB
testcase_19 AC 8 ms
6,940 KB
testcase_20 AC 9 ms
6,944 KB
testcase_21 AC 62 ms
6,940 KB
testcase_22 AC 3,297 ms
6,944 KB
testcase_23 AC 105 ms
6,940 KB
testcase_24 AC 4 ms
6,940 KB
testcase_25 AC 450 ms
6,940 KB
testcase_26 AC 2 ms
6,940 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
constexpr int INF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr double EPS = 1e-8;
constexpr int MOD = 998244353;
constexpr int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1};
constexpr int dy8[] = {1, 1, 0, -1, -1, -1, 0, 1}, dx8[] = {0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U> inline bool chmax(T &a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U> inline bool chmin(T &a, U b) { return a > b ? (a = b, true) : false; }
struct IOSetup {
  IOSetup() {
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << fixed << setprecision(20);
  }
} iosetup;

template <int MOD>
struct MInt {
  unsigned int val;
  MInt(): val(0) {}
  MInt(long long x) : val(x >= 0 ? x % MOD : x % MOD + MOD) {}
  static constexpr int get_mod() { return MOD; }
  static void set_mod(int divisor) { assert(divisor == MOD); }
  static void init(int x = 10000000) { inv(x, true); fact(x); fact_inv(x); }
  static MInt inv(int x, bool init = false) {
    // assert(0 <= x && x < MOD && std::__gcd(x, MOD) == 1);
    static std::vector<MInt> inverse{0, 1};
    int prev = inverse.size();
    if (init && x >= prev) {
      // "x!" and "MOD" must be disjoint.
      inverse.resize(x + 1);
      for (int i = prev; i <= x; ++i) inverse[i] = -inverse[MOD % i] * (MOD / i);
    }
    if (x < inverse.size()) return inverse[x];
    unsigned int a = x, b = MOD; int u = 1, v = 0;
    while (b) {
      unsigned int tmp = a / b;
      std::swap(a -= tmp * b, b);
      std::swap(u -= tmp * v, v);
    }
    return u;
  }
  static MInt fact(int x) {
    static std::vector<MInt> f{1};
    int prev = f.size();
    if (x >= prev) {
      f.resize(x + 1);
      for (int i = prev; i <= x; ++i) f[i] = f[i - 1] * i;
    }
    return f[x];
  }
  static MInt fact_inv(int x) {
    static std::vector<MInt> finv{1};
    int prev = finv.size();
    if (x >= prev) {
      finv.resize(x + 1);
      finv[x] = inv(fact(x).val);
      for (int i = x; i > prev; --i) finv[i - 1] = finv[i] * i;
    }
    return finv[x];
  }
  static MInt nCk(int n, int k) {
    if (n < 0 || n < k || k < 0) return 0;
    if (n - k > k) k = n - k;
    return fact(n) * fact_inv(k) * fact_inv(n - k);
  }
  static MInt nPk(int n, int k) { return n < 0 || n < k || k < 0 ? 0 : fact(n) * fact_inv(n - k); }
  static MInt nHk(int n, int k) { return n < 0 || k < 0 ? 0 : (k == 0 ? 1 : nCk(n + k - 1, k)); }
  MInt pow(long long exponent) const {
    MInt tmp = *this, res = 1;
    while (exponent > 0) {
      if (exponent & 1) res *= tmp;
      tmp *= tmp;
      exponent >>= 1;
    }
    return res;
  }
  MInt &operator+=(const MInt &x) { if((val += x.val) >= MOD) val -= MOD; return *this; }
  MInt &operator-=(const MInt &x) { if((val += MOD - x.val) >= MOD) val -= MOD; return *this; }
  MInt &operator*=(const MInt &x) { val = static_cast<unsigned long long>(val) * x.val % MOD; return *this; }
  MInt &operator/=(const MInt &x) { return *this *= inv(x.val); }
  bool operator==(const MInt &x) const { return val == x.val; }
  bool operator!=(const MInt &x) const { return val != x.val; }
  bool operator<(const MInt &x) const { return val < x.val; }
  bool operator<=(const MInt &x) const { return val <= x.val; }
  bool operator>(const MInt &x) const { return val > x.val; }
  bool operator>=(const MInt &x) const { return val >= x.val; }
  MInt &operator++() { if (++val == MOD) val = 0; return *this; }
  MInt operator++(int) { MInt res = *this; ++*this; return res; }
  MInt &operator--() { val = (val == 0 ? MOD : val) - 1; return *this; }
  MInt operator--(int) { MInt res = *this; --*this; return res; }
  MInt operator+() const { return *this; }
  MInt operator-() const { return MInt(val ? MOD - val : 0); }
  MInt operator+(const MInt &x) const { return MInt(*this) += x; }
  MInt operator-(const MInt &x) const { return MInt(*this) -= x; }
  MInt operator*(const MInt &x) const { return MInt(*this) *= x; }
  MInt operator/(const MInt &x) const { return MInt(*this) /= x; }
  friend std::ostream &operator<<(std::ostream &os, const MInt &x) { return os << x.val; }
  friend std::istream &operator>>(std::istream &is, MInt &x) { long long val; is >> val; x = MInt(val); return is; }
};
namespace std { template <int MOD> MInt<MOD> abs(const MInt<MOD> &x) { return x; } }
using ModInt = MInt<MOD>;

struct UnionFind {
  std::vector<int> data;

  UnionFind(int n) : data(n, -1) {}

  int root(int ver) { return data[ver] < 0 ? ver : data[ver] = root(data[ver]); }

  bool unite(int u, int v) {
    u = root(u);
    v = root(v);
    if (u == v) return false;
    if (data[u] > data[v]) std::swap(u, v);
    data[u] += data[v];
    data[v] = u;
    return true;
  }

  bool same(int u, int v) { return root(u) == root(v); }

  int size(int ver) { return -data[root(ver)]; }
};

int c;

map<pair<string, char>, pair<vector<int>, unordered_set<int>>> memo1;
pair<vector<int>, unordered_set<int>> f(const string &v, char ub) {
  if (memo1.count({v, ub}) == 1) return memo1[{v, ub}];
  vector<vector<int>> no(c + 1);
  UnionFind base(c * 2 + 1);
  REP(j, c) {
    if (v[j] == '0') {
      base.unite(j, c * 2);
    } else {
      no[v[j] - '0'].emplace_back(j);
    }
  }
  FOR(i, 1, c + 1) {
    FOR(j, 1, no[i].size()) base.unite(no[i][j - 1], no[i][j]);
    no[i].clear();
  }
  unordered_set<int> emp;
  FOR(j, 1, c - 1) {
    if ('1' <= v[j] && v[j] <= ub) emp.emplace(base.root(j));
  }
  return memo1[{v, ub}] = {base.data, emp};
}

int main() {
  int r, n; cin >> r >> c >> n;
  map<tuple<string, char, int>, ModInt> dp{{{string(c, '0'), '0', 0}, 1}};
  ModInt ans = 0;
  REP(ri, r + 1) {
    map<tuple<string, char, int>, ModInt> nx;
    for (auto [fst, pat] : dp) {
      auto [v, ub, edge] = fst;
      auto [base, emp] = f(v, ub);
      REP(i, 1 << c) {
        if (ri == 0 && i == 0) continue;
        if (ri == r && i > 0) break;
        // cout << v << '\n';
        // REP(j, c) cout << (i >> j & 1);
        // cout << '\n';
        UnionFind uf(c * 2 + 1);
        REP(j, c * 2 + 1) uf.data[j] = base[j];
        int e = edge;
        REP(j, c) {
          if (i >> j & 1) {
            if (v[j] > ub) uf.unite(j, c + j);
            if (j > 0 && (i >> (j - 1) & 1)) uf.unite(c + j - 1, c + j);
          } else {
            if (v[j] <= ub) uf.unite(j, c + j);
            if (j > 0 && !(i >> (j - 1) & 1)) uf.unite(c + j - 1, c + j);
          }
        }
        if (!(i & 1)) uf.unite(c, c * 2);
        if (!(i >> (c - 1) & 1)) uf.unite(c + c - 1, c * 2);
        unordered_set<int> nx_emp{uf.root(c * 2)};
        FOR(j, 1, c - 1) {
          if (!(i >> j & 1)) nx_emp.emplace(uf.root(c + j));
        }
        bool illegal = false;
        for (int e : emp) {
          if (nx_emp.count(uf.root(e)) == 0) {
            illegal = true;
            break;
          }
        }
        if (illegal) {
          // cout << "hall\n";
          continue;
        }
        for (int j = 0; j < c;) {
          if (!(i >> j & 1)) {
            ++j;
            continue;
          }
          e += v[j] <= ub || (j > 0 && v[j - 1] > ub);
          int k = j;
          while (k < c && (i >> k & 1)) {
            e += v[k] <= ub && (k == j || v[k - 1] > ub);
            ++k;
          }
          e += v[k - 1] <= ub || (k < c && v[k] > ub);
          j = k;
        }
        REP(j, c) e += v[j] > ub && !(i >> j & 1) && (j == 0 || v[j - 1] <= ub || (i >> (j - 1) & 1));
        // cout << "edge: " << edge << " -> " << e << '\n';
        if (e > n) continue;
        if (i == 0) {
          if (e < n) continue;
          unordered_set<int> root;
          REP(j, c) {
            if (v[j] > ub) root.emplace(uf.root(j));
          }
          // cout << "connected?: " << (root.size() == 1) << '\n';
          if (root.size() == 1) ans += pat * (r - ri + 1);
        } else {
          unordered_set<int> adj;
          REP(j, c) {
            if (i >> j & 1) adj.emplace(uf.root(c + j));
          }
          REP(j, c) {
            if (v[j] > ub && adj.count(uf.root(j)) == 0) {
              illegal = true;
              break;
            }
          }
          if (illegal) {
            // cout << "separate\n";
            continue;
          }
          string u = "";
          unordered_map<int, int> mp{{uf.root(c * 2), 0}};
          REP(j, c) {
            if (!(i >> j & 1)) {
              int root = uf.root(c + j);
              if (mp.count(root) == 0) {
                int size = mp.size();
                mp[root] = size;
              }
            }
          }
          char nx_ub = '0' + (mp.size() - 1);
          REP(j, c) {
            if (i >> j & 1) {
              int root = uf.root(c + j);
              if (mp.count(root) == 0) {
                int size = mp.size();
                mp[root] = size;
              }
            }
          }
          REP(j, c) u += '0' + mp[uf.root(c + j)];
          nx[{u, nx_ub, e}] += pat;
          // cout << "next: " << u << '\n';
        }
      }
    }
    dp.swap(nx);
    // for (auto [fst, pat] : dp) {
    //   auto [v, edge] = fst;
    //   cout << v << ' ' << edge << " : " << pat << '\n';
    // }
    // cout << '\n';
  }
  cout << ans << '\n';
  return 0;
}
0