题解 ARC118E【Avoid Permutations】/ SS240928D【d】

题目描述

对于一个排列 a,定义其权值如下:
生成一个 (n+2)×(n+2) 的网格图,行列标号为 0n+1,每次可以从 (i,j) 走到 (i,j+1)(i+1,j),且不能走到 (i,ai),权值为从 (0,0) 走到 (n+1,n+1) 的方案数。
现在排列 a 某些位置被改成了 1,请求出所有可能的初始排列的权值之和 mod998244353

n200

solution

枚举最终路径,考虑有多少个排列能使这个路径走过去。这样的话可以 dp,考虑记录当前走到哪个格子,但是这样1 的位置带来的信息会很多,要一直保持不经过 1 的对应位置,状态数容易起飞(其实是可以做的),所以考虑我们不考虑 1 的位置,开始容斥(???)。施子集容斥,钦定经过其中 x 个原来为 1 的点,其它 1 点不管,最后乘上 (cnt1x)!,不是 1 的照常限制。只有在路径上的 1 点是有作用的。进行动态规划,记录当前所在格子,已经经过 x1 点,为了防止重复在同一行或列放 1 点,再记录两个 bit 表示这一行、列是否已经填过 1。转移枚举这个格子上是否放 1 点或者往下、右走一步。总复杂度 O(n3)

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
template <unsigned umod>
struct modint {/*{{{*/
  static constexpr int mod = umod;
  unsigned v;
  modint() = default;
  template <class T> modint(const T& y) : v(y % mod + (y < 0 ? mod : 0)) {}
  modint& operator+=(const modint& rhs) { v += rhs.v; if (v >= umod) v -= umod; return *this; }
  modint& operator-=(const modint& rhs) { v -= rhs.v; if (v >= umod) v += umod; return *this; }
  modint& operator*=(const modint& rhs) { v = (unsigned)(1ull * v * rhs.v % umod); return *this; }
  friend modint operator+(modint lhs, const modint& rhs) { return lhs += rhs; }
  friend modint operator-(modint lhs, const modint& rhs) { return lhs -= rhs; }
  friend modint operator*(modint lhs, const modint& rhs) { return lhs *= rhs; }
  friend int raw(const modint& self) { return self.v; }
  friend ostream& operator<<(ostream& os, const modint& self) { return os << raw(self); }
};/*}}}*/
using mint = modint<998244353>;
int n, a[210];
bool vis[210];
mint f[210][210][210][2][2], fac[210];
mint ans = 0;
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  cin >> n;
  for (int i = 1; i <= n; i++) cin >> a[i];
  int cnt = n;
  vis[0] = vis[n + 1] = true;
  for (int i = 1; i <= n; i++) if (~a[i]) vis[a[i]] = true, cnt -= 1;
  for (int i = raw(fac[0] = 1); i <= n; i++) fac[i] = fac[i - 1] * i;
  f[0][0][0][0][0] = 1;
  for (int i = 0; i <= n + 1; i++) {
    for (int j = 0; j <= n + 1; j++) if (i < 1 || i > n || a[i] != j) {
      for (int k = 0; k <= cnt; k++) {
        if (a[i] == -1 && !vis[j]) f[i][j][k + 1][1][1] += f[i][j][k][0][0];
        for (int lf : {0, 1}) {
          for (int cf : {0, 1}) {
            if (raw(f[i][j][k][lf][cf])) debug("f[%d][%d][%d][%d][%d] = %d\n", i, j, k, lf, cf, raw(f[i][j][k][lf][cf]));
            f[i + 1][j][k][0][cf] += f[i][j][k][lf][cf];
            f[i][j + 1][k][lf][0] += f[i][j][k][lf][cf];
          }
        }
      }
    }
  }
  for (int k = 0; k <= cnt; k++) ans += (k & 1 ? -1 : 1) * fac[cnt - k] * f[n + 1][n + 1][k][0][0];
  cout << ans << endl;
  return 0;
}

posted @   caijianhong  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示