CodeForces 1909F2 Small Permutation Problem (Hard Version)
感觉这个题还是挺不错的。
考虑 F1。考察 差分后的意义,发现 就是 。
考虑将其转化为棋盘问题。在 放一个车,那么 就是 和 这些格子组成的“L”字形的车的数量。
一个放车的方案合法当且仅当所有车互不攻击。因此容易发现合法的 一定 。考虑从前往后扫,同时维护答案 和现在还没被占用的行(列)数量 。
- 若 ,无事发生,多了第 行和列没被占用,因此 。
- 若 ,相当于可以在 和 中还没被占用的格子放车,同时也可以在 放车,那么 , 不变。
- 若 , 和 中还没被占用的格子各放一个车,那么 ,然后 。
如上讨论可以通过 F1。
F2 我们继续将其放到棋盘上考虑。考虑一个 的位置 ,设它上一个 的位置是 。现在相当于求在 的左下角抠掉了 的一块的“L”字形棋盘放 个互不攻击的车的方案数,其中 。每个这样的“L”字形互相独立,所以可以直接把方案乘起来。
上面的问题可以考虑容斥(我现在还不会不容斥的做法?)。相当于在左下角 的区域不能放车,那么我钦定 个车放在了左下角,设 为 的棋盘放 个互不攻击的车的方案数,那么这部分的方案为 ,容斥系数为 ,因此结果为:
最后一个问题是求 。考虑先选 行放车,有 种选列的方案,那么 。
容易发现 ,所以时间复杂度为 。
code
// Problem: F2. Small Permutation Problem (Hard Version) // Contest: Codeforces - Pinely Round 3 (Div. 1 + Div. 2) // URL: https://codeforces.com/contest/1909/problem/F2 // Memory Limit: 256 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> #define pb emplace_back #define fst first #define scd second #define mkp make_pair #define mems(a, x) memset((a), (x), sizeof(a)) using namespace std; typedef long long ll; typedef double db; typedef unsigned long long ull; typedef long double ldb; typedef pair<ll, ll> pii; const int maxn = 200100; const ll mod = 998244353; inline ll qpow(ll b, ll p) { ll res = 1; while (p) { if (p & 1) { res = res * b % mod; } b = b * b % mod; p >>= 1; } return res; } ll n, fac[maxn], a[maxn], ifac[maxn]; inline ll A(ll n, ll m) { if (n < m || n < 0 || m < 0) { return 0; } else { return fac[n] * ifac[n - m] % mod; } } inline ll C(ll n, ll m) { if (n < m || n < 0 || m < 0) { return 0; } else { return fac[n] * ifac[m] % mod * ifac[n - m] % mod; } } inline ll F(ll n, ll m) { return C(n, m) * A(n, m) % mod; } void solve() { scanf("%lld", &n); for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); } fac[0] = 1; for (int i = 1; i <= n; ++i) { fac[i] = fac[i - 1] * i % mod; } ifac[n] = qpow(fac[n], mod - 2); for (int i = n - 1; ~i; --i) { ifac[i] = ifac[i + 1] * (i + 1) % mod; } a[0] = 0; if (a[n] != -1 && a[n] != n) { puts("0"); return; } a[n] = n; int j = 0; ll ans = 1; for (int i = 1; i <= n; ++i) { if (a[i] != -1) { int t = a[i] - a[j], x = j - a[j], y = i - a[j]; if (t < 0) { puts("0"); return; } ll res = 0; for (int i = 0; i <= x && i <= t; ++i) { res = (res + ((i & 1) ? mod - 1 : 1) * F(x, i) % mod * F(y - i, t - i)) % mod; } ans = ans * res % mod; j = i; } } printf("%lld\n", ans); } int main() { int T = 1; scanf("%d", &T); while (T--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】