题意简述:如果一个排列P满足对于所有的i都有(|P_i-i| eq k),则称排列P为合法的。现给出n(排列长度)和k,求有多少种合法的排列,对924844033取模。
clj课件的第一题呢,简单讲下做法。
首先我们看到计数先考虑容斥,答案可以写成(sum_{i=0}^{n}(-1)^{i}g[i]*(n-i)!),其中(g[i])表示有i个不合法的方案,当前瓶颈在于如何求(g[i])。
然后我们构造一个位置集合和权值集合,发现对于任意一个位置(P),我们将(P)与数值为(P+k,P-k)连边,其与无论是位置还是数值,当且仅当它们对(k)取余相等时,才会对互相造成影响。然后我们容易发现,这是一个左边为位置,右边为权值的二分图,且有一堆互不干扰的链,问题变成了求任选n条边的方案数。我们直接将这些链处理出来,然后设(f[i][j][0/1])表示当前考虑到第(i)个元素,已经选了(j)条边,当前边选或不选,简单转移即可。
然后(g[i]=f[n*2][i][0]+f[n*2][i][1]),就可以解了。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2010,mod=924844033;
int n,k,g[N<<1],f[N<<1][N][2],cnt,fac[N],ans;
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++){
for(int j=i;j<=n;j+=k)g[++cnt]=(j==i);
for(int j=i;j<=n;j+=k)g[++cnt]=(j==i);
}
f[0][0][0]=fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=1LL*i*fac[i-1]%mod;
for(int i=0;i<n*2;i++)
for(int j=0;j<=min(i,n);j++){
f[i+1][j][0]=(f[i][j][0]+f[i][j][1])%mod;
if(!g[i+1])f[i+1][j+1][1]=f[i][j][0];
}
for(int i=0;i<=n;i++){
int tmp=1LL*(f[n<<1][i][0]+f[n<<1][i][1])*fac[n-i]%mod;
ans=(mod+(ans+(i&1?-1:1)*tmp)%mod)%mod;
}
printf("%d
",ans);
}
agc005_d