[AGC005D]~K Perm Counting-二分图-动态规划

来源:互联网 发布:淘宝客服链接 编辑:程序博客网 时间:2024/06/06 08:24

~K Perm Counting

Problem Statement

Snuke loves permutations. He is making a permutation of length N.

Since he hates the integer K, his permutation will satisfy the following:

Let the permutation be a1,a2,…,aN. For each i=1,2,…,N, |ai−i|≠K.
Among the N! permutations of length N, how many satisfies this condition?

Since the answer may be extremely large, find the answer modulo 924844033(prime).

Constraints

2≦N≦2000
1≦K≦N−1

Input

The input is given from Standard Input in the following format:

N K

Output

Print the answer modulo 924844033.

Sample Input 1

3 1

Sample Output 1

2

2 permutations (1,2,3), (3,2,1) satisfy the condition.

Sample Input 2

4 1

Sample Output 2

5

5 permutations (1,2,3,4), (1,4,3,2), (3,2,1,4), (3,4,1,2), (4,2,3,1) satisfy the condition.


神题一道……
这无比风骚的操作真是令人震惊和赞叹啊。


题意:
求所有满足条件的1到n的排列a,使得对于任意a[i],满足|a[i]i|k

思路:
考虑把值和位置之间的关系建成一个二分图。
假定左半的N个点依次代表值1~N,右半的N个点依次代表位置1~N。
那么,一条边(i,j)存在的意义是,位置j处的值是i
可以发现所有不合法的情况均包含形如(i,ik)(i,i+k)的一条边。

g[i]表示钦定i个不合法的位置,其他位置未知的方案数。
考虑剩下的位置未知,那么采用容斥,最终结果为:
ans=n!ni=0g[i](ni)!(1)i

那么剩下的就是如何计算g[i]了。
考虑建出只含所有不合法边的方案数
可以发现这个二分图将会是如下的样子(k=3):(想象相同编号的位置之间存在一条边)

1  42  53  64  15  26  31  42  53  6  

这样的极其有规律的折线便是能找到的所有匹配。
考虑把所有这样的链以任意顺序串起来,一起dp:
f[i][j][0/1],描述当前在i号节点(左右共2n个),已经选择了j条匹配边,当前点连向链上的下一个点的边是选了(1)还是没选(0)的方案数。
那么可以得到转移方程:
f[i+1][j][0]=f[i][j][0]+f[i][j][1]
f[i+1][j+1][1]=f[i][j][0]

然而考虑到咱现在是把所有链串在一起DP,链与链相交的地方是没有边的。
那么按照把所有连串起来的顺序,记录每个链的结尾,在结尾处强制跳过1的状态的转移即可。
最后g[i]=f[2n][i][0]+f[2n][i][1],大功告成!

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>using namespace std;typedef long long ll;const int N=4009;const int md=924844033;int n,k;bool vis[N][N];int ed[N],tot;int f[N][N][2];int g[N],fac[N];int main(){    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)        for(int j=0;j<=1;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;                ed[tot+=len]=1;            }    f[1][0][0]=1;    for(int i=1;i<=tot;i++)        for(int j=0;j<=n;j++)        {            f[i+1][j][0]=(f[i][j][0]+f[i][j][1])%md;            if(!ed[i])                f[i+1][j+1][1]=f[i][j][0];        }    for(int i=0;i<=n;i++)        g[i]=(f[tot][i][0]+f[tot][i][1])%md;    fac[0]=1;    for(int i=1;i<=n;i++)        fac[i]=(ll)fac[i-1]*i%md;    int ans=fac[n];    for(int i=1,j=-1;i<=n;i++,j=-j)        ans=((ll)ans+md+(ll)j*fac[n-i]*g[i]%md)%md;    printf("%lld\n",ans);    return 0;}