难得能独立推出容斥题好激动啊。
这么鬼畜的条件显然不能直接$DP$,考虑容斥。
设$f(i)$为至少$i$个位置不满足条件的方案数。
这玩意还是不好直接求。
$|P_i-i|\neq k\leftrightarrow P_i\neq i+k \lor P_i\neq i-k$
一个位置有两种取值是不合法的。
当两个位置$i,j$同时不合法且$P_i=P_j$时,必然满足$i+k=j-k$。
也就是说$i$与$i+2k$存在一个共同的不合法值$i+k$。
把这些不合法值连起来(假设$k=2$):
这时我们可以对每条这样的链$DP$。
设$g(i,j,0/1/2)$为考虑到$i$,$i$所在的链选了$j$个不合法值,$i$选择了$i-k/i+k$或不选。
普及转移:
$g(i,j,0)=g(i-2k,j-1,0)+g(i-2k,j-1,2)$
$g(i,j,1)=g(i-2k,j-1,0)+g(i-2k,j-1,1)+g(i-2k,j-1,2)$
$g(i,j,2)=g(i-2k,j,0)+g(i-2k,j,1)+g(i-2k,j,2)$
考虑合并所有链,做个背包就行了:
$f(i)=\sum\limits_{j=1}^i f(i-j)\times (g(k,j,0)+g(k,j,1)+g(k,j,2))(k\in [\max\{n-2k+1,1\},n])$
最后容斥一波,由于求的$f(i)$是仅考虑选不合法位置的方案数,还要给剩下位置随机钦定。
$ans=\sum\limits_{i=0}^n(-1)^if(i)(n-i)!$
我的写法不能处理$k=0$的情况要特判,这时其实就是错排。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 2005
#define inf 0x3f3f3f3f
const int mod = 924844033;
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int f[maxn],c[maxn][maxn],g[maxn][maxn][3],fac[maxn]={1},n,m,ans;
inline long long pow1(int x){return x&1?-1ll:1ll;}
int main(){
n=read(),m=read();
for(register int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod;
if(!m){
c[0][0]=1;
for(register int i=1;i<=n;++i){
c[i][0]=1;
for(register int j=1;j<=i;++j)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
for(register int i=0;i<=n;++i)
(ans+=pow1(i)*c[n][i]*fac[n-i]%mod)%=mod;
printf("%d\n",(ans+mod)%mod);
return 0;
}
for(register int i=1;i<=min(m<<1,n);++i){
g[i][0][2]=1;
if(i-m>0)g[i][1][0]=1;
if(i+m<=n)g[i][1][1]=1;
}
for(register int i=(m<<1)+1;i<=n;++i){
int x=i-(m<<1);
for(register int j=0;j<=n;++j){
if(j&&i-m>0)g[i][j][0]=(g[x][j-1][0]+g[x][j-1][2])%mod;
if(j&&i+m<=n)g[i][j][1]=((g[x][j-1][0]+g[x][j-1][1])%mod+g[x][j-1][2])%mod;
g[i][j][2]=((g[x][j][0]+g[x][j][1])%mod+g[x][j][2])%mod;
}
}
f[0]=1;
int sum=0,cnt;
for(register int i=max(n-(m<<1)+1,1);i<=n;++i){
cnt=(i-1)/(m<<1)+1,sum+=cnt;
for(register int k=1;k<=cnt;++k)
for(register int j=sum;j;--j)
for(register int k=min(j,cnt);k;--k)
(f[j]+=1ll*f[j-k]*((g[i][k][0]+g[i][k][1])%mod+g[i][k][2])%mod)%=mod;
}
for(register int i=0;i<=n;++i)
(ans+=pow1(i)*f[i]*fac[n-i]%mod)%=mod;
printf("%d\n",(ans+mod)%mod);
}