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]=(imi)

然后把 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;
}
posted @ 2021-10-18 19:56  lahlah  阅读(31)  评论(0编辑  收藏  举报