题目大意
求有多少中1~n的排列,使得 abs(第i个位置的值−i)!=k a b s ( 第 i 个 位 置 的 值 − i ) ! = k
解题思路
考虑容斥,
ans=∑ni=0(−1)ig[i](n−i)!(g[i]表示至少有i个位置是不合法的方案数)
a
n
s
=
∑
i
=
0
n
(
−
1
)
i
g
[
i
]
(
n
−
i
)
!
(
g
[
i
]
表
示
至
少
有
i
个
位
置
是
不
合
法
的
方
案
数
)
考虑如何求g[i]
将每个位置和每个值都作为一个点,有2n个点,如果第i位置不可以填j,将位置i向值j连边。
这样,就得到了一个二分图,问题就变成了选i条边的方案数。
将二分图的每条链拉出来,并在一起,就形成2n个点排成一排,一些相邻点之间有边。
设
f[i][j][0/1]
f
[
i
]
[
j
]
[
0
/
1
]
表示,做到第i个点,选了j条边,这个点与上个一点的边是否有选(如果没边就为0)的方案数。
那么
g[i]=f[2n][i]][0]+f[2n][i][1]
g
[
i
]
=
f
[
2
n
]
[
i
]
]
[
0
]
+
f
[
2
n
]
[
i
]
[
1
]
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <bitset>
#include <set>
const int inf=2147483647;
const int mo=924844033;
const int N=4005;
using namespace std;
int n,m,tot;
bool lk[N];
long long f[N][N][2],jc[N],ans;
int main()
{
scanf("%d%d",&n,&m);
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mo;
for(int i=1;i<=m;i++)
for(int t=2;t--;)
for(int j=i;j<=n;j+=m)
{
tot++;
if(j!=i) lk[tot]=1;
}
f[0][0][0]=1;
for(int i=0;i<n*2;i++)
for(int j=0;j<=n;j++)
{
f[i+1][j][0]=(f[i][j][0]+f[i][j][1])%mo;
if(lk[i+1]) f[i+1][j+1][1]=f[i][j][0];
}
for(int i=0,t=1;i<=n;i++,t=-t)
{
f[2*n][i][0]=1ll*(f[2*n][i][0]+f[2*n][i][1])*jc[n-i]%mo;
ans=(ans+f[2*n][i][0]*t+mo)%mo;
}
printf("%lld\n",ans);
}