题目链接
https://agc005.contest.atcoder.jp/tasks/agc005_d
题意简述
求长度为的排列中的排列总数。
题解
假设满足的位置的数量至少有个的排列总数为。那么答案就是
现在的问题就是怎么求。建一个二分图,如果两个点满足:
- 分别在二分图的两侧
- 假设编号为,满足
那么就连一条边。这张图上匹配为的方法的总数就是。
那么?容易发现,这张图是由多条链构成的,将这几条链拼在一起,然后在钦定一些地方不能有匹配的边,这张二分图的匹配数量为的方案数可以很方便的dp求出。
代码
#include <cstdio>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=2000;
const int mod=924844033;
int n,k,t,vis[maxn+10][2],a[maxn*2+10],f[maxn*2+10][maxn+10][2],g[maxn+10],ans,fac[maxn+10];
int main()
{
n=read();
k=read();
for(int i=1; i<=n; ++i)
{
for(int j=0; j<2; ++j)
{
if(!vis[i][j])
{
int len=0;
for(int x=i,y=j; x<=n; x+=k,y^=1)
{
vis[x][y]=1;
++len;
}
t+=len;
a[t+1]=1;
}
}
}
f[1][0][0]=1;
for(int i=2; i<=t; ++i)
{
f[i][0][0]=f[i-1][0][0]+f[i-1][0][1];
if(f[i][0][0]>=mod)
{
f[i][0][0]-=mod;
}
for(int j=1; j<=n; ++j)
{
f[i][j][0]=f[i-1][j][0]+f[i-1][j][1];
if(f[i][j][0]>=mod)
{
f[i][j][0]-=mod;
}
if(!a[i])
{
f[i][j][1]=f[i-1][j-1][0];
}
}
}
for(int i=0; i<=n; ++i)
{
g[i]=f[t][i][0]+f[t][i][1];
if(g[i]>=mod)
{
g[i]-=mod;
}
}
fac[0]=1;
for(int i=1; i<=n; ++i)
{
fac[i]=1ll*fac[i-1]*i%mod;
}
for(int i=0; i<=n; ++i)
{
ans=(ans+1ll*((i&1)?(mod-1):1)*g[i]%mod*fac[n-i])%mod;
}
printf("%d\n",ans);
return 0;
}