设 (f_{n,k}) 为逆序对为 (k) 的 (n) 阶排序数量,其生成函数为 (F_n(x)=sumlimits_{kgeqslant 0} f_{n,k} x^k),考虑排列的第 (i) 个位置,通过决定其与前 (i-1) 个位置的相对大小,得其对逆序对的贡献范围为 ([0,i-1]),因此有:
[large F_n(x)=prod_{i=1}^nsum_{j=0}^{i-1}x^j=prod_{i=1}^nfrac{1-x^i}{1-x}
]
节点深度转化为有多少个节点为该节点的祖先,得 (j) 为 (i) 在笛卡尔树上的祖先的充要条件是 (p_j) 是区间 ([min(i,j),max(i,j)]) 的最小值。考虑刚才构造排列的方法,从 (i) 开始向 (j) 的方向或反方向,决定每个位置的相对大小,得 (j<i) 时,(j) 不产生逆序对,(j>i) 时,(j) 产生 (j-i) 个逆序对。每次先让 (i) 位置对逆序对的贡献在区间 ([0,i-1]) 内,然后枚举 (j),撤销掉贡献即可。
#include<bits/stdc++.h>
#define maxn 50010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k,p,m;
int f[maxn],g[maxn];
int main()
{
read(n),read(k),read(p),m=n*(n-1)/2,f[0]=1;
for(int i=1;i<=n;++i)
{
for(int j=m;j>=i;--j) f[j]=(f[j]-f[j-i]+p)%p;
for(int j=1;j<=m;++j) f[j]=(f[j]+f[j-1])%p;
}
for(int i=1;i<=n;++i) g[i]=f[k];
for(int i=2;i<=n;++i)
{
for(int j=m;j;--j) f[j]=(f[j]-f[j-1]+p)%p;
for(int j=i;j<=m;++j) f[j]=(f[j]+f[j-i])%p;
for(int j=1;j+i-1<=n;++j)
{
if(k-i+1>=0) g[j]=(g[j]+f[k-i+1])%p;
g[j+i-1]=(g[j+i-1]+f[k])%p;
}
for(int j=m;j>=i;--j) f[j]=(f[j]-f[j-i]+p)%p;
for(int j=1;j<=m;++j) f[j]=(f[j]+f[j-1])%p;
}
for(int i=1;i<=n;++i) printf("%d ",g[i]);
return 0;
}