CodeForces 1909F2 Small Permutation Problem (Hard Version)

洛谷传送门

CF 传送门

感觉这个题还是挺不错的。

考虑 F1。考察 ai 差分后的意义,发现 aiai1 就是 (j=1i1[pj=i])+pii

考虑将其转化为棋盘问题。在 (i,pi) 放一个车,那么 aiai1 就是 (1,i)(i,i)(i,1)(i,i1) 这些格子组成的“L”字形的车的数量。

一个放车的方案合法当且仅当所有车互不攻击。因此容易发现合法的 aiai1 一定 [0,2]。考虑从前往后扫,同时维护答案 ans 和现在还没被占用的行(列)数量 t

  • ai=ai1,无事发生,多了第 i 行和列没被占用,因此 tt+1
  • aiai1=1,相当于可以在 (1,i)(i1,i)(i,1)(i,i1) 中还没被占用的格子放车,同时也可以在 (i,i) 放车,那么 ansans×(2t+1)t 不变。
  • aiai1=2(1,i)(i1,i)(i,1)(i,i1) 中还没被占用的格子各放一个车,那么 ansans×t2,然后 tt1

如上讨论可以通过 F1

F2 我们继续将其放到棋盘上考虑。考虑一个 ai1 的位置 i,设它上一个 aj1 的位置是 j。现在相当于求在 y×y 的左下角抠掉了 x×x 的一块的“L”字形棋盘放 t 个互不攻击的车的方案数,其中 x=jaj,y=iaj,t=aiaj。每个这样的“L”字形互相独立,所以可以直接把方案乘起来。

上面的问题可以考虑容斥(我现在还不会不容斥的做法?)。相当于在左下角 x×x 的区域不能放车,那么我钦定 i 个车放在了左下角,设 F(n,m)n×n 的棋盘放 m 个互不攻击的车的方案数,那么这部分的方案为 F(x,i)×F(yi,ti),容斥系数为 (1)i,因此结果为:

i=0t(1)iF(x,i)F(yi,ti)

最后一个问题是求 F(n,m)。考虑先选 m 行放车,有 n!(nm)! 种选列的方案,那么 F(n,m)=(nm)×n!(nm)!

容易发现 t=n,所以时间复杂度为 O(n)

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;
}
posted @   zltzlt  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示