AT2062 [AGC005D] ~K Perm Counting
https://www.luogu.com.cn/problem/AT2062
考虑建一个二分图,然后向
i
+
/
−
k
i+/-k
i+/−k连边
抽出来若干条链,考虑计算钦定
j
j
j条边的方案数,最后二项式反演一波即可
对于每条链单独考虑
f
[
i
]
f[i]
f[i]表示这条链选
i
i
i条边的方案数
由插板法可以容易得到对于一个
m
m
m个节点的链,
f
[
i
]
=
(
m
−
i
i
)
f[i]=\binom{m-i}{i}
f[i]=(im−i)
然后把
f
f
f的
O
G
F
OGF
OGF卷起来即可
code:
#include<bits/stdc++.h>
#define mod 924844033
#define N 200050
#define ll long long
using namespace std;
ll qpow(ll x, int y) {
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
ll fac[N], ifac[N];
void init(int n) {
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 >= 0; i --) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
ll C(int n, int m) {
if(n < m) return 0;
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int n, k;
ll f[N], g[N];
int main() {
scanf("%d%d", &n, &k);
init(4 * max(n, k));
f[0] = 1;
for(int i = 1; i <= min(n, 2 * k); i ++) {
int m = 0;
for(int j = (i - 1) % k + 1; j <= n; j += k) m ++;
m --;
for(int j = 0; j <= n; j ++) g[j] = f[j], f[j] = 0;
for(int j = 0; j <= m; j ++) {
int o = C(m + 1 - j, j);
for(int k = 0; j + k <= n; k ++) {
f[j + k] = (f[j + k] + 1ll * o * g[k] % mod) % mod;
}
}
}
ll ans = 0;
for(int i = 0; i <= n; i ++) {
if(i & 1) ans = (ans - 1ll * fac[n - i] * f[i] % mod + mod) % mod;
else ans = (ans + 1ll * fac[n - i] * f[i] % mod) % mod;
}
printf("%lld", ans);
return 0;
}