洛谷P5853 [USACO19DEC] Tree Depth P
节点深度转化为有多少个节点为该节点的祖先,得 \(b\) 为 \(a\) 在笛卡尔树上的祖先的充要条件是 \(p_b\) 是区间 \([\min(a,b),\max(a,b)]\) 的最小值。原问题就转化为对于每个 \(b\) 统计有多少个逆序对为 \(k\) 的 \(n\) 阶排序满足 \(p_b\) 是区间 \([\min(a,b),\max(a,b)]\) 的最小值。
设 \(f_{n,k}\) 为逆序对数为 \(k\) 的 \(n\) 阶排序数量,其生成函数为 \(F(x)=\sum\limits_{k\geqslant 0} f_{n,k} x^k\),考虑排列的第 \(i\) 个位置,通过决定其与前 \(i-1\) 个位置的相对大小,得其对逆序对数的贡献范围为 \([0,i-1]\),因此有:
\[\large F(x)=\prod_{i=1}^n\sum_{j=0}^{i-1}x^j=\prod_{i=1}^n\frac{1-x^i}{1-x}
\]
考虑刚才构造排列的方法,从 \(a\) 开始向 \(b\) 的方向,决定每个位置的相对大小,决定的顺序是任意,只需保证任何时刻排列都是连续的即可。
\(a>b\) 时,\(b\) 不产生逆序对,其对应的生成函数为:
\[\large F_1(x)=\prod_{i=1}^n\frac{1-x^i}{1-x}\frac{1-x}{1-x^{a-b+1}}
\]
\(a<b\) 时,\(b\) 产生 \(b-a\) 个逆序对,其对应的生成函数为:
\[\large F_2(x)=\prod_{i=1}^n\frac{1-x^i}{1-x}\frac{1-x}{1-x^{b-a+1}}x^{b-a}
\]
先求出 \(F(x)\),然后枚举 \(|a-b+1|\),每次进行多项式除法,来计算 \(F_1(x),F_2(x)\) 的贡献。总复杂度为 \(O(n^3)\)。
#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;
}