嘘~ 正在从服务器偷取页面 . . .

[AGC005D] ~K Perm Counting 题解


[AGC005D] ~K Perm Counting 题解

题目链接:[AGC005D] ~K Perm Counting

题意

如果一个排列 \(P\) 满足对于所有的 \(i\) 都有 \(|P_i-i|\neq k\),则称排列 \(P\) 为合法的。

现给出 \(n\)\(k\),求有多少种合法的排列。

由于答案很大,请输出答案对 \(924844033\) 取模的结果。

数据范围

\(2\leq n\leq 2\times 10^3\)\(1\leq k\leq n-1\)

如果我们把每个点不能放的位置画成一行,那么可以得到下图(\(n=7,k=2\) ,图来自参考文献1)

图中阴影部分是不可以放的,并且每行每列都只能有一个格子被选中(可以认为是放 \(n\) 个互不攻击的车)

正难则反,考虑容斥。设 \(f_i\) 为钦定 \(i\) 个阴影格子放车。答案为 \[ \sum_{i=0}^n(-1)^if_i\cdot (n-i)! \] 那么 \(f_i\) 怎么求呢?根据定义,\(f_i\) 的选择要求车之间互不攻击。

如果我们把每个阴影格子与其不能同时选的格子连边,可以得到下图

那么 \(f_i\) 就是这张图的独立集个数。注意到这张图由多条链构成。

考虑重新设 \(f(i,j)\) 为前 \(i\) 个位置,选了 \(j\) 个点构成独立集的方案数,边界 \(f(0,0)=1\) ,那么 \[ f(i,0) = f(i-1,0) \\[6pt]f(i,j) = f(i-1,j) + f(i-1-[\lnot a_{i-1}],\, j-1)\quad (j > 0) \] 这里 \(a_{i-1}\) 表示 \(i-1\)\(i\) 是否不相连,这个是可以线性求出来的。

时间复杂度 \(\mathcal{O}(n^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 924844033;
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
template<typename T> void add(T &x, T y) { (x += y) >= mod ? x -= mod : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e3 + 15))

bool a[N * 2];
int n, m, fac[N], f[N * 2][N];;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; fac[0] = 1;
    rep(i, 1, n) fac[i] = fac[i - 1] * i % mod;
    int t = 0; a[0] = true;
    rep(i, 1, (n - m) % m)
    {
        a[t += (n - m) / m + 1] = true;
        a[t += (n - m) / m + 1] = true;
    }
    rep(i, 1, m - (n - m) % m)
    {
        a[t += (n - m) / m] = true;
        a[t += (n - m) / m] = true;
    }
    int res = 0; f[0][0] = 1;
    rep(i, 1, t) rep(j, 0, n)
        add(f[i][j] = f[i - 1][j], j ? f[i - 1 - (!a[i - 1])][j - 1] : 0);
    rep(j, 0, n)
        add(res, f[t][j] * fac[n - j] % mod * ((j & 1) ? mod - 1 : 1) % mod);
    cout << res << '\n';
    return 0;
}

这道题还可以用多项式什么的 \(\mathcal{O}(n\log n)\) ,这里不讲了(因为不会


参考文献

[1] https://www.luogu.com.cn/article/u5s75fn2

超级感谢 Roundgod 对我的帮助!


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录