AGC005D ~K Perm Counting

Link
考虑建立一个左右侧均有\(n\)个点的完全二分图,设左右侧点分别为\(L_1,\cdots,L_n,R_1,\cdots R_n\)
显然这个二分图的完美匹配与\(n\)个元素的排列一一对应,具体而言设\(L_i\leftrightarrow R_{p_i}\),那么这个完美匹配就与排列\(\{p\}\)对应。
而题目给定的限制要求就相当于去掉\((L_i,R_{i\pm k})\)这些边。
\(f_i\)表示该二分图中大小为\(i\)的匹配个数,那么答案就是\(\sum\limits_{i=0}^n(-1)^i(n-i)!f_i\)
利用dp可以轻松地在\(O(n^2)\)的时间复杂度下求出\(f\),不难发现可以利用多项式优化至\(O(n\log n)\)

#include<cstdio>
const int N=4007,P=924844033;
int f[N][N][2],vis[N];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
void dec(int&a,int b){a-=b,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int main()
{
    int n,k,x,y=0,ans=0;
    scanf("%d%d",&n,&k),x=(n-1)/k+1,f[0][0][1]=1;
    for(int i=((n-1)%k+1)*2;i;--i) vis[y+=x]=1;
    for(;y<2*n;) vis[y+=x-1]=1;
    for(int i=1;i<=2*n;++i)
	for(int j=0;j<=n;++j)
	{
	    f[i][j][0]=f[i][j][1]=f[i-1][j][1];
	    if(!vis[i-1]&&j) inc(f[i][j][1],f[i-1][j-1][0]);
	}
    for(int i=0,k=1;i<=n;k=mul(k,++i)) ((n-i)&1? dec:inc)(ans,mul(f[2*n][n-i][1],k));
    printf("%d",ans);
}
posted @ 2020-04-13 23:00  Shiina_Mashiro  阅读(131)  评论(0编辑  收藏  举报