定义 个区间 构成的序列是好的,当且仅当:
- ;
- 存在唯一的一个 的排列 使得 ;
给定 ,求好的区间序列个数,对素数 取模。
,。
首先钦定 ,最后答案再乘上 ,那么这样有 。
考虑将 和 分开做,那么限制相当于不存在 使得 ,否则就可以交换 。
十分智慧地,考虑二维平面。对于每个 ,加入线段 和线段 ,那么限制转化为这些线段不能在除 外的地方相交。
那么考虑先画出横着的线段,将 下方的区域涂上颜色,竖着的线段的限制就是不能进入被涂色的区域:(图来自 luogu @隔壁泞2的如心 的题解)

不难发现,对于最右边的未被涂色的列,则其左侧和右侧是独立的;而若不存在这样的列,最下面一行除了 外一定都被涂色,相当于一个 的子问题:

考虑 dp,设 表示大小为 的答案, 表示大小为 且最下面一行除了 外都被涂色的答案,则有转移:
的转移是枚举最右边的未被涂色的列在哪里; 的转移则是枚举第 行最右边的未被涂色的列在哪里,从而确认 的最小值。
边界 ,时间复杂度 ,代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=5005;
int n,p;
int g[S],f[S];
inline void add(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
int main()
{
scanf("%d%d",&n,&p);
g[1]=1,f[1]=1;
for(int i=2;i<=n;i++)
{
g[i]=1ll*(i-1)*g[i-1]%p;
for(int j=2;j<=i-1;j++)
{
add(g[i],1ll*f[j-1]*g[i-j]%p*j%p*(i-j)%p);
}
f[i]=g[i];
for(int j=2;j<=i;j++)
{
add(f[i],1ll*f[j-1]*g[i-j+1]%p*j%p);
}
}
int fra=1;
for(int i=1;i<=n;i++) fra=1ll*fra*i%p;
printf("%d\n",1ll*f[n]*fra%p);
return 0;
}