我们记一个排列 P P P的升高为 k k k当且仅当存在 k k k个位置 i i i使得 P i < P i + 1 P_i < P_{i+1} Pi<Pi+1。
现在给定排列长度 n n n,对于所有整数 k ∈ [ 0 , n ] k\in [0,n] k∈[0,n],求有多少个排列的升高为 k k k。
n ≤ 200000 n\le 200000 n≤200000
考虑钦定 k k k个位置为 < < <,其他位置不管。对 ( n − 1 k ) \binom{n-1}{k} (kn−1)种不同取法的方案数求和,记为 g k g_k gk。
如果把关系为 < < <的相邻两个位置合并,则共有 n − k n-k n−k个集合,记第 i i i个集合的大小为 s i s_i si,则合法的排列数量为 n ! ∏ s i ! \frac{n!}{\prod s_i!} ∏si!n!
因此 g k = ∑ s i n ! ∏ s i ! g_k=\sum_{s_i}\frac{n!}{\prod s_i!} gk=si∑∏si!n!
写成生成函数的形式,就是 g k = n ! [ x n ] ( e x − 1 ) m = n ! [ x n ] ∑ i = 0 m ( m i ) ( − 1 ) m − i e i x = n ! ∑ i = 0 m ( m i ) ( − 1 ) m − i i n i ! \begin{aligned} g_k&=n! [x^n] (e^x-1)^m\\ &=n! [x^n] \sum_{i=0}^m\binom{m}{i}(-1)^{m-i}e^{ix}\\ &=n! \sum_{i=0}^m\binom{m}{i}(-1)^{m-i}\frac{i^n}{i!} \end{aligned} gk=n![xn](ex−1)m=n![xn]i=0∑m(im)(−1)m−ieix=n!i=0∑m(im)(−1)m−ii!in
把组合数展开然后FFT就可以求出 g k g_k gk。
设 f k f_k fk表示答案,不难发现 g i = ∑ j = i n − 1 ( j i ) f j g_i=\sum_{j=i}^{n-1}\binom{j}{i}f_j gi=j=i∑n−1(ij)fj
二项式反演一下就可以得到 f i = ∑ j = i n − 1 ( j i ) ( − 1 ) j − i g j f_i=\sum_{j=i}^{n-1}\binom{j}{i}(-1)^{j-i}g_j fi=j=i∑n−1(ij)(−1)j−igj
再做一次FFT即可。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
令 f n , m f_{n,m} fn,m表示长度为 n n n,恰好有 m m m个 < < <的排列数, f n , m f_{n,m} fn,m也称欧拉数。
不难得到递推公式 f n , k = ( k + 1 ) f n − 1 , k + ( n − k ) f n − 1 , k − 1 f_{n,k}=(k+1)f_{n-1,k}+(n-k)f_{n-1,k-1} fn,k=(k+1)fn−1,k+(n−k)fn−1,k−1
欧拉数还有如下通项 f n , m = ∑ k = 0 m ( n + 1 k ) ( − 1 ) k ( m + 1 − k ) n f_{n,m}=\sum_{k=0}^m \binom{n+1}{k}(-1)^k(m+1-k)^n fn,m=k=0∑m(kn+1)(−1)k(m+1−k)n
可以通过数学归纳法证明上述通项
f n , m = ( m + 1 ) f n − 1 , m + ( n − m ) f n − 1 , m − 1 f_{n,m}=(m+1)f_{n-1,m}+(n-m)f_{n-1,m-1} fn,m=(m+1)fn−1,m+(n−m)fn−1,m−1
= ( m + 1 ) ∑ k = 0 m ( n k ) ( − 1 ) k ( m + 1 − k ) n − 1 + ( n − m ) ∑ k = 0 m − 1 ( n k ) ( − 1 ) k ( m − k ) n − 1 =(m+1)\sum_{k=0}^m\binom{n}{k}(-1)^k(m+1-k)^{n-1}+(n-m)\sum_{k=0}^{m-1}\binom{n}{k}(-1)^k(m-k)^{n-1} =(m+1)k=0∑m(kn)(−1)k(m+1−k)n−1+(n−m)k=0∑m−1(kn)(−1)k(m−k)n−1
= ∑ k = 0 m ( n k ) ( − 1 ) k [ ( m + 1 ) ( m + 1 − k ) n − 1 + ( n − m ) ( m − k ) n − 1 ] =\sum_{k=0}^m\binom{n}{k}(-1)^k[(m+1)(m+1-k)^{n-1}+(n-m)(m-k)^{n-1}] =k=0∑m(kn)(−1)k[(m+1)(m+1−k)n−1+(n−m)(m−k)n−1]
= ∑ k = 0 m ( n k ) ( − 1 ) k [ ( m + 1 − k ) n + k ( m + 1 − k ) n − 1 + ( n − m ) ( m − k ) n − 1 ] =\sum_{k=0}^{m}\binom{n}{k}(-1)^k[(m+1-k)^n+k(m+1-k)^{n-1}+(n-m)(m-k)^{n-1}] =k=0∑m(kn)(−1)k[(m+1−k)n+k(m+1−k)n−1+(n−m)(m−k)n−1]
展开后最左边那项就是我们想要的 ∑ k = 0 m ( n + 1 k ) ( − 1 ) k ( m + 1 − k ) n \sum_{k=0}^m\binom{n+1}{k}(-1)^k(m+1-k)^n k=0∑m(kn+1)(−1)k(m+1−k)n
考虑后面两项
∑ k = 0 m ( n k ) ( − 1 ) k ( n − m ) ( m − k ) n − 1 + ∑ k = 0 m ( n k ) ( − 1 ) k ( m + 1 − k ) n [ k m + 1 − k − k n + 1 − k ] \sum_{k=0}^m\binom{n}{k}(-1)^k(n-m)(m-k)^{n-1}+\sum_{k=0}^m\binom{n}{k}(-1)^k(m+1-k)^n\Big[\frac{k}{m+1-k}-\frac{k}{n+1-k}\Big] k=0∑m(kn)(−1)k(n−m)(m−k)n−1+k=0∑m(kn)(−1)k(m+1−k)n[m+1−kk−n+1−kk]
= ( n − m ) ∑ k = 0 m ( n k ) ( − 1 ) k ( m − k ) n − 1 + ∑ k = 0 m ( n k ) ( − 1 ) k ( m + 1 − k ) n k n − m ( m + 1 − k ) ( n + 1 − k ) =(n-m)\sum_{k=0}^m\binom{n}{k}(-1)^k(m-k)^{n-1}+\sum_{k=0}^m\binom{n}{k}(-1)^k(m+1-k)^nk\frac{n-m}{(m+1-k)(n+1-k)} =(n−m)k=0∑m(kn)(−1)k(m−k)n−1+k=0∑m(kn)(−1)k(m+1−k)nk(m+1−k)(n+1−k)n−m
前面那项当 k = m k=m k=m时贡献为 0 0 0,用 k − 1 k-1 k−1替换 k k k,可以得到上式等于
( n − m ) ∑ k = 1 m ( n k − 1 ) ( − 1 ) k − 1 ( m − k + 1 ) n − 1 + ( n − m ) ∑ k = 0 m ( n k ) ( − 1 ) k ( m + 1 − k ) n − 1 k n + 1 − k (n-m)\sum_{k=1}^m\binom{n}{k-1}(-1)^{k-1}(m-k+1)^{n-1}+(n-m)\sum_{k=0}^m\binom{n}{k}(-1)^k(m+1-k)^{n-1}\frac{k}{n+1-k} (n−m)k=1∑m(k−1n)(−1)k−1(m−k+1)n−1+(n−m)k=0∑m(kn)(−1)k(m+1−k)n−1n+1−kk
不难发现上式就等于 0 0 0。
发现是个卷积形式,直接FFT就好了。
#include
typedef long long LL;
const int N=200005;
const int MOD=998244353;
int n,jc[N],ny[N],g[N],a[N*4],b[N*4],rev[N*4],L;
void pre(int n)
{
int lg=0;
for (L=1;L<=n;L<<=1,lg++);
for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
void NTT(int *a,int f)
{
for (int i=0;i<L;i++) if (i<rev[i]) std::swap(a[i],a[rev[i]]);
for (int i=1;i<L;i<<=1)
{
int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2);
for (int j=0;j<L;j+=(i<<1))
{
int w=1;
for (int k=0;k<i;k++)
{
int u=a[j+k],v=(LL)a[j+k+i]*w%MOD;
a[j+k]=(u+v)%MOD;a[j+k+i]=(u+MOD-v)%MOD;
w=(LL)w*wn%MOD;
}
}
}
if (f==-1) for (int i=0,inv=ksm(L,MOD-2);i<L;i++) a[i]=(LL)a[i]*inv%MOD;
}
int main()
{
scanf("%d",&n);
jc[0]=jc[1]=ny[0]=ny[1]=1;
for (int i=2;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=n;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
for (int i=0;i<=n;i++) a[i]=(LL)ksm(i,n)*ny[i]%MOD,b[i]=(i&1)?MOD-ny[i]:ny[i];
pre(n*2);
NTT(a,1);NTT(b,1);
for (int i=0;i<L;i++) a[i]=(LL)a[i]*b[i]%MOD;
NTT(a,-1);
for (int i=0;i<n;i++) g[i]=(LL)jc[n-i]*a[n-i]%MOD;
for (int i=0;i<L;i++) a[i]=b[i]=0;
for (int i=0;i<=n;i++) a[n-i]=(i&1)?MOD-ny[i]:ny[i],b[i]=(LL)g[i]*jc[i]%MOD;
NTT(a,1);NTT(b,1);
for (int i=0;i<L;i++) a[i]=(LL)a[i]*b[i]%MOD;
NTT(a,-1);
for (int i=0;i<n;i++) printf("%d ",(LL)a[i+n]*ny[i]%MOD);
puts("0");
return 0;
}
#include
typedef long long LL;
const int N=200005;
const int MOD=998244353;
int n,jc[N],ny[N],g[N],a[N*4],b[N*4],rev[N*4],L;
void pre(int n)
{
int lg=0;
for (L=1;L<=n;L<<=1,lg++);
for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
void NTT(int *a,int f)
{
for (int i=0;i<L;i++) if (i<rev[i]) std::swap(a[i],a[rev[i]]);
for (int i=1;i<L;i<<=1)
{
int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2);
for (int j=0;j<L;j+=(i<<1))
{
int w=1;
for (int k=0;k<i;k++)
{
int u=a[j+k],v=(LL)a[j+k+i]*w%MOD;
a[j+k]=(u+v)%MOD;a[j+k+i]=(u+MOD-v)%MOD;
w=(LL)w*wn%MOD;
}
}
}
if (f==-1) for (int i=0,inv=ksm(L,MOD-2);i<L;i++) a[i]=(LL)a[i]*inv%MOD;
}
int main()
{
scanf("%d",&n);
jc[0]=jc[1]=ny[0]=ny[1]=1;
for (int i=2;i<=n+1;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=n+1;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
for (int i=0;i<=n+1;i++) a[i]=ksm(i+1,n),b[i]=(LL)jc[n+1]*ny[i]%MOD*ny[n+1-i]%MOD,b[i]=(i&1)?MOD-b[i]:b[i];
pre(n*2);
NTT(a,1);NTT(b,1);
for (int i=0;i<L;i++) a[i]=(LL)a[i]*b[i]%MOD;
NTT(a,-1);
for (int i=0;i<n;i++) printf("%d ",a[i]);
puts("0");
return 0;
}