题目大意

原题长的一批的翻译:

有一种有向图,被称为排列的DRG图,其包含组节点,第包含个节点:

DRG上有2种边:组内边和组间边。
组内的组内边可以表示为:(应该都能看懂)
图片说明
组间边可以表示为:
图片说明

现在我们想知道排列的DRG图的拓扑序列的数量。
有向图的拓扑序列可以表示为:
所有节点来自且对于任意,图片说明
答案对素数取模。

简单的说

给定一个有向无环图-晒肉架图DRG(n),求DRG(n)有多少种拓扑序列。

拓扑序列:
1.从图中选择一个没有前驱(即入度为0)的顶点并输出。
2.从图中删除该顶点(即加入拓扑序列)和所有以它为起点的有向边。
3.重复1,2,直到图为空或图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

解题思路

先来举例说明DRG图到底是个啥。这是DRG(4)的图,烤肉架图名不虚传:
图片说明

略微观察一下,我们可以发现每个肉片只和架子的两个节点连接。当这两个节点被删除,肉片就会分离,而肉片作为一个子图的拓扑序列是非常容易计算的。
所以,本题做法要重点维护架子以及连在架子上的肉片的状态。

可以发现在按拓扑序列删除节点的过程中,这张图的特征可以分为以下三种情况:

h(n)

第一种是一个完整的DRG(n)只删除了第一个节点,我们设这样的情况为(在代码中因为是最后输出,改成了ans)。
图片说明

是删了第一个节点,那么接下来只有两种删除方法,删去第一行的节点或者删去第一列的节点。
分别会变成以及(往下看)。

f(i,j)

第二种是在完整的DRG(n)中删除了第一行中两个以上的连续节点,设第一行剩余节点数为,第二行节点数为,我们设这种情况为f(i,j)。
图片说明

接下来也只有两种删除方法,分别是变为以及中的其中一种(因为此时有一个肉片掉了)。

g(i,j)

第三种是在完整的DRG(n)中删除了第一行和第二行的第一个节点,然后连续删除了第一块肉片的左半边部分。设第一行的剩余节点数为,以及第一块肉片左半边剩余节点数,我们设这种情况为g(i,j)。
图片说明

也只有两种删除操作,分别是变成以及(肉片掉了)。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=3010,M=18000010;
long long a[M],b[M];
int f[N][N],g[N][N],w[N][N],ans[N],n,m,mod;
int get(int x,int y)
{
    return a[x]*b[y]%mod*b[x-y]%mod;
}
int main()
{
    int x,y,i,j;
    scanf("%d%d",&n,&mod);
    m=n*n*2,a[0]=a[1]=b[0]=b[1]=ans[0]=1;
    for(i=2;i<=m;i++)
        a[i]=a[i-1]*i%mod,b[i]=mod-(mod/i)*b[mod%i]%mod;
    for(i=1;i<=m;i++)
        b[i]=b[i-1]*b[i]%mod;
    for(i=0;i<=n;i++)
        for(j=i;j<=n;j++)
        {
            if(i==0) w[i][j]=1;
            else
            {
                w[i][j]=w[i-1][j];
                if(i<j) w[i][j]+=w[i][j-1];
                if(w[i][j]>=mod) w[i][j]-=mod;
            }
        }
    for(j=2;j<=n;j++)
    {
        if(j>=3) y=f[0][j-1];
        else y=ans[0];
        x=(j-1)*(n*2+1)-n*2;
        (f[0][j]+=1ll*w[n][n]*y%mod*get(x+n*2,n*2)%mod)%=mod;
    }
    for(i=1;i<n;i++)
    {
        for(j=0;j<=n;j++)
        {
            x=2*i-1+(i-1)*(n*2);
            g[i][j]=1ll*w[j][n]*ans[i-1]%mod*get(n+j+x,x)%mod;
            if(j!=0) (g[i][j]+=g[i][j-1])%=mod;
        }
        ans[i]=f[i-1][i+1]+g[i][n];
        if(ans[i]>=mod) ans[i]-=mod;
        for(j=2;j<=n;j++)
        {
            f[i][j]=f[i-1][j];
            if(j-1>i+1) y=f[i][j-1];
            else y=ans[i];
            x=i+(j-1)*(n*2+1)-n*2;
            (f[i][j]+=1ll*w[n][n]*y%mod*get(x+n*2,n*2)%mod)%=mod;
        }
    }
    printf("%d\n",ans[n-1]);
    return 0;
}