题解 P5825 【排列计数】
题意
我的题面写的挺直白的,不再多过解释。
题解
对于长度为
其实这个东西的英文名叫
首先我们有一个非常
考虑从长度为
然后有四种情况。
-
在排列的左端插入一个
n ,这样不产生任何升高,所以这种情况从\left\langle\begin{matrix}n-1\\k\end{matrix}\right\rangle 转移来。 -
在排列的右端插入一个
n ,这样会产生一个升高,所以这种情况由\left\langle\begin{matrix}n-1\\k-1\end{matrix}\right\rangle 转移来。 -
在某一个位置
i 和i+1 中间插入n ,其中P_i<P_{i+1} ,那么这里会产生一个升高,同时会破坏原来的一个升高,所以总的升高是不变的。所以之前的排列要有k 个升高,所以,这种情况由k\left\langle\begin{matrix}n-1\\k\end{matrix}\right\rangle 转移来。 -
在某一个位置
i 和i+1 中间插入n ,其中P_i>P_{i+1} ,那么这里会产生一个升高,但是不会破坏任何原有的升高,所以总而言之产生了一个升高。所以之前的排列要有k-1 个升高,所以,这种情况由(n-k-1)\left\langle\begin{matrix}n-1\\k-1\end{matrix}\right\rangle 转移来。
所以,我们有一个简单的递推式:
然后,我们来证明一个非常重要的恒等式叫做
在证明这个之前,我们先来证明一个小小的东西做热身:
两边强行展开一下就好了,然后我们用数学归纳法证。
首先,要证明的柿子在
然后,若
于是,我们来推推柿子,首先从一个很弱智的东西开始:
然后我们热身的东西就派上用场啦:
一步拆括号,有
然后,康康右边,我们用之前的递推式,有
意识到在
右边的和式用
然后发现这个
然后就是有限微积分啊哈哈哈。首先我们注意到
然后做多次差分即可得到这样一个东西:
很好,于是我们可以前进一步了
我们将
于是,有
很好,我们又迈出了一步。接下来是关键的一步。
我们两边对
乘上
然后你会发现是个二项式定理的事情,然后简单的代换一下最终我们得到
很好。强行拆组合数
然后,写得更加清楚一点,我们有
现在,我们设
把
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=524351,MOD=998244353,G=3,INVG=332748118;
ll fd,cnt,limit;
ll f[MAXN],g[MAXN],rev[MAXN],fact[MAXN],finv[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline ll qpow(ll base,ll exponent)
{
li res=1;
while(exponent)
{
if(exponent&1)
{
res=1ll*res*base%MOD;
}
base=1ll*base*base%MOD,exponent>>=1;
}
return res;
}
inline void NTT(ll *cp,ll cnt,ll inv)
{
ll cur=0,res=0,omg=0;
for(register int i=0;i<cnt;i++)
{
if(i<rev[i])
{
swap(cp[i],cp[rev[i]]);
}
}
for(register int i=2;i<=cnt;i<<=1)
{
cur=i>>1,res=qpow(inv==1?G:INVG,(MOD-1)/i);
for(register ll *p=cp;p!=cp+cnt;p+=i)
{
omg=1;
for(register int j=0;j<cur;j++)
{
ll t=1ll*omg*p[j+cur]%MOD,t2=p[j];
p[j+cur]=(t2-t+MOD)%MOD,p[j]=(t2+t)%MOD;
omg=1ll*omg*res%MOD;
}
}
}
if(inv==-1)
{
ll invl=qpow(cnt,MOD-2);
for(register int i=0;i<=cnt;i++)
{
cp[i]=1ll*cp[i]*invl%MOD;
}
}
}
inline void setup(ll cnt)
{
fact[0]=finv[0]=1;
for(register int i=1;i<cnt;i++)
{
fact[i]=(li)fact[i-1]*i%MOD;
}
finv[cnt-1]=qpow(fact[cnt-1],MOD-2);
for(register int i=cnt-2;i;i--)
{
finv[i]=(li)finv[i+1]*(i+1)%MOD;
}
}
int main()
{
setup((fd=read()+1)+10);
for(register int i=0;i<fd;i++)
{
f[i]=(li)(i&1?MOD-1:1)*finv[i]%MOD;
g[i]=(li)qpow(i,fd-1)*finv[i]%MOD;
}
cnt=1,limit=-1;
while(cnt<(fd<<1))
{
cnt<<=1,limit++;
}
for(register int i=0;i<cnt;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(f,cnt,1),NTT(g,cnt,1);
for(register int i=0;i<cnt;i++)
{
f[i]=(li)f[i]*g[i]%MOD,g[i]=0;
}
NTT(f,cnt,-1);
for(register int i=0;i<fd;i++)
{
f[i]=(li)f[i]*fact[i]%MOD*fact[fd-1-i]%MOD;
g[i]=(fd-1-i)&1?MOD-finv[fd-1-i]:finv[fd-1-i];
}
for(register int i=fd;i<cnt;i++)
{
f[i]=g[i]=0;
}
reverse(g,g+fd),NTT(f,cnt,1),NTT(g,cnt,1);
for(register int i=0;i<cnt;i++)
{
f[i]=(li)f[i]*g[i]%MOD;
}
NTT(f,cnt,-1),reverse(f,f+fd);
for(register int i=0;i<fd;i++)
{
printf("%d ",(li)f[i]*finv[i]%MOD);
}
}