[容斥] [dp] [AGC005D] ~K Perm Counting
posted on 2024-05-09 05:44:53 | under | source
不建图,硬做!
考虑容斥,令钦定 \(i\) 个非法的方案数为 \(g_i\),显然 \(ans=\sum\limits_{i\in[0,n]}(-1)^i (n-i)!g_i\)。
非法实际指 \(a_i=i\pm k\)。所以只有模 \(k\) 下相同的 \(i\) 会产生冲突,考虑枚举 \(i\bmod k=p\)。
现在这个问题就简单了。每次有三种选择:\(+k\)、\(-k\)、不选,因为不能冲突,所以还要知道 \(i-k\) 是否被选。于是定义 \(f_{i,j,1/0,1/0}\) 表示当前考虑 \(a_{(i-1)*k+p}\) 填什么,填了 \(j\) 个,\(a_i\) 是否 \(= i+k\),\((i-1)*k+p\) 是否被选走。
求完 \(f\) 后,合并进 \(g\) 即可。
转移不难。复杂度:注意到每个 \(p\) 只会枚举 \(\frac nk\) 个 \(i\),求 \(f\) 是 \(O(k*\frac nk *\frac nk)=O(\frac {n^2}k)\),求 \(g\) 是 \(O(k*n*\frac nk)=O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ADD(a, b) a = (a + (b)) % mod
const int N = 2e3 + 5, mod = 924844033;
int n, k, c, ci;
int f[N][N][2][2], g[N], g2[N], ans, jc[N];
signed main(){
jc[0] = 1; for(int i = 1; i < N; ++i) jc[i] = jc[i - 1] * i % mod;
cin >> n >> k, c = n / k + 1;
g[0] = 1;
for(int p = 1; p <= k; ++p){
for(int i = 0; i <= c; ++i)
for(int j = 0; j <= i; ++j) f[i][j][0][0] = f[i][j][0][1] = f[i][j][1][0] = f[i][j][1][1] = 0;
f[0][0][0][0] = 1, ci = (n - p) / k + 1;
for(int i = 1; i <= ci; ++i)
for(int j = 0; j <= i; ++j){
//摆烂
ADD(f[i][j][0][0], f[i - 1][j][0][0] + f[i - 1][j][0][1]);
ADD(f[i][j][0][1], f[i - 1][j][1][0] + f[i - 1][j][1][1]);
if(j){
if(i > 1){ //后
ADD(f[i][j][0][0], f[i - 1][j - 1][0][0]);
ADD(f[i][j][0][1], f[i - 1][j - 1][1][0]);
}
if(i < ci){ //前
ADD(f[i][j][1][0], f[i - 1][j - 1][0][0] + f[i - 1][j - 1][0][1]);
ADD(f[i][j][1][1], f[i - 1][j - 1][1][0] + f[i - 1][j - 1][1][1]);
}
}
}
memset(g2, 0, sizeof g2);
for(int i = n; ~i; --i)
for(int j = ci; ~j; --j)
if(i + j <= n)
ADD(g2[i + j], g[i] * ((f[ci][j][0][0] + f[ci][j][0][1] + f[ci][j][1][0] + f[ci][j][1][1]) % mod) % mod);
for(int i = 0; i <= n; ++i) g[i] = g2[i];
}
for(int i = 0, sb = 1; i <= n; ++i, sb = -sb) ADD(ans, sb * g[i] * jc[n - i] % mod + mod);
cout << ans;
return 0;
}

浙公网安备 33010602011771号