洛谷P5853 [USACO19DEC] Tree Depth P
节点深度转化为有多少个节点为该节点的祖先,得 b 为 a 在笛卡尔树上的祖先的充要条件是 pb 是区间 [min 的最小值。原问题就转化为对于每个 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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步