一个点的深度等于树中它祖先的个数(包括自己)。
那么我们可以对于一个点对 ((x,y)) 考虑在所有排列中 (y) 做了几次 (x) 的祖先。
在笛卡尔树上如果 (y) 是 (x) 的祖先那么 (a_y) 是 (a_{x..y}) 中的最小值。(这里现设 (xle y) )
那么我们可以求:有多少个逆序对个数为 (K) 的排列,满足 (a_y) 是 (a_{x..y}) 中的最小值((xle y))。
前置1:有多少个逆序对个数为 (K) 的 (n) 元排列。
先放结论:答案是 (prodlimits_{i=1}^{n}sumlimits_{j=0}^{i-1}t^i) 的 (t^K) 次项系数,这里用到了生成函数知识。
证明1:
- 我们考虑相对大小。从左到右安排每一个位置,在考虑第 (i) 个数时,它前面有 (i-1) 个数,它可以成为第 (1 ext{~}i) 小的任意一个数,增加的逆序对个数为 (0 ext{~}i-1)。最小时新增逆序对为 (i-1) ;第 (i) 小时,它就是最大的数,新增的逆序对数为 0。
- 从右到左安排也是可以的,只不过考虑它可以成为第 (1 ext{~}i-1) 大的数字中的任意一个,逆序对增加的量也是 (0 ext{~}i-1) 。所以我们可以往前安排也可以往后安排,就是说想放哪放哪。记住这一段话。
证明2:
- 我们考虑相对位置。从小到大安排每一个数字,在考虑数字 (i) 时,比它小的数排成一排,它可以插入在第 (1 ext{~}i) 中任意一个位置中,插入到最前面时,新增的逆序对个数为 (i-1);插入到最后面时,新增的逆序对个数为 0。
- 其实这个证明不看也是可以的,对这个题好像没什么帮助,但证明方法越多越好,说不定以后什么题就需要这个证明2。
所以不管怎么放数字,放第 (i) 个时,逆序对新增的数目可以为 (0 ext{~}i-1) 中的任意一个,把它当成一个多项式。全部乘起来取第 (K) 项系数就行了。(但是直接乘是会 TLE 的,由于这些多项式是特殊的,指数是连续的,有更高妙的方法将他们乘起来,这个放到后面讲。)。
然后返回原始的问题:有多少个逆序对个数为 (K) 的排列,满足 (a_y) 是 (a_{x..y}) 中的最小值((xle y))。
将排列分成 ([1,x-1]cup[x,y-1]cup{y}cup[y+1,n]) 四部分,分成三类处理:
- 先考虑 ([x,y-1]) 这一块。显然,逆序对为 (K) 的排列有 (prodlimits_{i=1}^{y-x}sumlimits_{j=0}^{i-1}t^i) 的 (K) 次项系数个。
- 再考虑 ({y}) ,由于我们假设 (a_y) 是 (a_{x..y}) 中的最小值((xle y)),那么逆序对个数加 (y-x) 个。
- 最后考虑 ([1,x-1]) 和 ([y+1,n]) 这两块,用证明1的那个构造方法,我们要将第 (y-x+2) 个到第 (n) 个数字安排好。让你记住的那句话告诉我们,我们可以先放完 ([1,x-1]),再放完 ([y+1,n]);甚至我们可以 xjb 放,左边一个右边一个轮流来都可以。但是放的第 j 个数时可以新增的逆序对的范围始终是一样的,生成函数乘起来是 (sumlimits_{i=y-x+2}^nsumlimits_{j=0}^{i-1} t^i)。
把这 3 类的结果乘起来,就是:(prodlimits_{i=1}^{n}sumlimits_{j=0}^{i-1}t^i)
但是啊,你放的这个 (y) 造成的逆序对个数已经大于等于 (y-x) 了,上面的式子是错的,所以你需要将上述式子除以 (sumlimits_{i=0}^{y-x}t_i) ,结果是:
对,这是一组 ((x,y)) 的结果,但是光这个复杂度好像就很高了,而且还有除法。别急,上面我说过这些多项式是特殊的。
首先上面的分子是可以预处理出来的,分 (n) 次相乘。这个相乘是有特殊做法的。
假设我们要算
前两个相乘的结果是 (1+2x+2x^2+x^3)。
你拿 (1+x+x^2+x^3) 和 (1+2x+2x^2+x^3) 相乘的结果是:
观察每一项的系数,能观察出来:(a_i=sumlimits_{j=i-3}^j b_j),这里的 (a_i) 是 (1+3x+5x^2+6x^3+5x^4+3x^5+x^6) 的 (i) 次项系数,(b_j) 是 (1+2x+2x^2+x^3) 的 (j) 次项系数。意思是,新的 (a_i) 可以从之前的 (b_j) 的一部分转移过来,这一部分的大小和 (1+x+x^2+x^3) 的最高次幂有关,因为它的指数又是连续的,所以 (a_i) 可以从 (b_j) 连续的一部分转移过来。(a_i=sumlimits_{j=i-3}^j b_j) 这里的 3 就是 (1+x+x^2+x^3) 的最高次项指数。
所以乘法就转换成了区间求和,可以差分一下,再求个前缀和。
对于除法就相当于是乘法的逆过程。
假如我们有了 ((1+x) imes(1+x+x^2) imes(1+x+x^2+x^3)=1+3x+5x^2+6x^3+5x^4+3x^5+x^6) ,我们需要将它除以 (1+x+x^2) 。
根据乘法我们知道,(a_i=sumlimits_{j=i-2}^j b_j) ,那么我们现在需要做的操作就是将 (a_i) 减去 (a_i=sumlimits_{j=i-2}^{j-1} b_j)。
从 (i=0) 减到 (i=6) 得:
({1,3,5,6,5,3,1},i=0)
({1,2,5,6,5,3,1},i=1)
({1,2,2,6,5,3,1},i=2)
({1,2,2,2,5,3,1},i=3)
({1,2,2,2,1,3,1},i=4)
({1,2,2,2,1,0,1},i=5)
({1,2,2,2,1,0,0},i=6)
即:(1+2x+2x^2+2x^3+x^4),和 ((1+x) imes(1+x+x^2+x^3)) 的结果一样。
同样可以通过差分和前缀和处理。
注意:加法从后往前倒着处理(类似于01背包),除法从前往后处理。
这样每次乘除的复杂度是 ( ext{O}(n^2)) 的,枚举 (x,y) 后,总的复杂度是 ( ext{O}(n^4))。
代码片段:
for(int i=1;i<n;i++)mul(i);//分子
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++){//枚举 x,y
div(i-j);//除法
if(K>=(i-j))(ans[j]+=f[K-(i-j)])%=mod;
//这个意思是,因为你 a_i 是最大的,肯定会有 (i-j) 个逆序对,我们需要从其他地方找到 K-(i-j) 个逆序对。
mul(i-j);//乘法
}
足以 TLE 此题。所以我们需要优化这个。
注意到上面这个式子 (frac{prodlimits_{i=1}^{n}sumlimits_{j=0}^{i-1}t^i}{sumlimits_{i=0}^{y-x}t_i}) ,分母只有 (n-1) 种取值(只和 (y-x) 有关),所以我们可以枚举 (y-x),对于多组 ((x,y)) 同时求解答案。这样我们只需要 (n) 次乘除法,复杂度为 ( ext{O}(n^3)) 足以通过此题。
代码片段:
for(int i=1;i<n;i++){
div(i);
if(K>=i){
for(int j=1;j<=n-i;j++)
(ans[j]+=f[K-i])%=mod;
}
mul(i);
}
但是,这样做真的完了吗?
好像还没有,观察观察这句话:
那么我们可以求:有多少个逆序对个数为 (K) 的排列,满足 (a_y) 是 (a_{x..y}) 中的最小值((xle y))。
如果 (x>y) 呢?
那么排列就是 (a_y,dots,a_x),这时,你统计的是 (x) 的祖先的个数,而放入 (a_y) 不会增加逆序对。
我们需要对上面的代码做一些小改动:
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++){
div(i-j);
if(K>=(i-j))(ans[j]+=f[K-(i-j)])%=mod;
(ans[i]+=f[K])%=mod;//由于 a_j 是最小的 没有新增的逆序对,直接用 f[K],加到i上是因为,这时候j是i的祖先。
mul(i-j);
}
for(int i=1;i<n;i++){
div(i);
if(K>=i){
for(int j=1;j<=n-i;j++)
(ans[j]+=f[K-i])%=mod;
}
for(int j=1+i;j<=n;j++)//这里改动的原因和上面一模一样
(ans[j]+=f[K])%=mod;
mul(i);
}
但是,这样做真的完了吗?
好像还没有,观察观察这句话:
一个点的深度等于树中它祖先的个数(包括自己)。
自己呢???好像咱们没有统计,对于每个位置直接加上 (f[K]) 就行。
最终代码:
#include<bits/stdc++.h>
using namespace std;
int n,K,top,f[45000],mod,ans[310];
inline int read()
{
int x=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='0';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return w?-x:x;
}
void mul(int x)
{
for(int i=top;i>=0;i--)
(f[i+x+1]-=f[i])%=mod;
top+=x;
for(int i=1;i<=top+1;i++)
(f[i]+=f[i-1])%=mod;
}
void div(int x)
{
for(int i=top;i;i--)
(f[i]-=f[i-1])%=mod;
top-=x;
for(int i=0;i<=top;i++)
(f[i+x+1]+=f[i])%=mod;
}
int main()
{
f[0]=1;
n=read();K=read();mod=read();
for(int i=1;i<n;i++)mul(i);
for(int i=1;i<n;i++){
div(i);
if(K>=i){
for(int j=1;j<=n-i;j++)
(ans[j]+=f[K-i])%=mod;
}
for(int j=1+i;j<=n;j++)
(ans[j]+=f[K])%=mod;
mul(i);
}
for(int i=1;i<=n;i++)
printf("%d%c",((ans[i]+f[K])%mod+mod)%mod,"
"[i==n]);
}