洛谷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;
}
posted @ 2020-10-15 22:31  lhm_liu  阅读(282)  评论(0编辑  收藏  举报