[洛谷]-5825排列计数-欧拉数、NTT

https://www.luogu.com.cn/problem/P5825

题意:我们记一个排列 P 的升高为 k 当且仅当存在 k 个位置 i 使得 Pi<Pi+1

给定排列长度 n,对于所有整数 k[0,n], 求有多少个排列的升高为 k1n2×105.


题解:就是要我们求欧拉数(Eulerian Number)nk(对每个k),这里捋一捋欧拉数的性质。

边界

先看看边界:n0=1,即只能从小到大排,nn=0,这样很明显,因为n个数至多有n1个升高。

对称性

同样不难发现类似组合数那样的对称性:nk=nnk1,因为假设某个排列 Pk 个升高,那么就恰有 n1k 个下降,把排列翻转之后下降就变成了升高(升高也变成了下降)

递推形式

考虑递推形式的时候经常可以考虑增加一个元素产生的影响,假设想求nk=?

考虑n移走之后是什么情况,需要说明的是增减一个元素至多改变一个“升高”,因此只要考虑 n1kn1k1 ,如果原本有 k 个上升,那么 n 要么插入在上升的 pi<pi+1中间,要么插入在第一个位置之前,这一部分有 k+1 种位置可以选择。否则如果原本有k1个上升,那么就有(n1)1(k1)=nk1个下降,需要增加一个上升要么在下降的pi>pi+1的中间插入,要么在最后一个位置插入。综上:

nk=(k+1)n1k+(nk)n1k1

用递推形式看起来是可以O(nk)地算某个位置的值了…但是太慢了。

在这个题里就要求我们算一行(一列似乎没什么太好的做法?——)

容斥

这一部分来自于EI的一篇博客: https://www.luogu.com.cn/blog/EntropyIncreaser/solution-p5825 ,这里大概相当于做一个更详细的注解。

欲求nk,先考虑转化成算概率(最后按照全概率公式,乘上方案数 n! 即可)进一步等价到a1,,an(0,1) 恰有 k 个位置ai<ai+1的概率,做差分bi=aiai1(mod1),这样一来只有在ai<ai1的地方,bi=aiai1+1,否则bi=aiai1,如此 bi=an+(n1k),而 bi 的这种关系也可以证明是均匀分布(没有严格证),而an(0,1),所以bi(n1k,nk)

问题转化成:有 n 个随机变量x1,xn(0,1) 上均匀分布,xi(n1k,nk) 的概率。

更进一步只要考虑 xi<nk 的概率,所以归约成如下问题:

01dx101dxn[xi<t]

[0,1] 的限制是很麻烦,设 F(n,k,t) 表示有 n 个随机变量x1,xn(0,+),且强制kxi>1,满足xi<t 的概率,那么根据容斥原理,答案就是

k=0n(nk)(1)kF(n,k,t)=k=0t(nk)(1)kF(n,0,tk)=Δk=0t(nk)(1)kG(n,tk)

因此只要考虑:

G(n,t)=0dx10dxn[i=1nxi<t]=0tdxn0dx10dxn1[i=1n1xi<txn]=0tdxn0(txnt)dx10(txnt) dxn1[i=1n1xi<t]=F(n1,t)0t(txnt)n1dxn=F(n1,t)×tn=tnn!

因此最终答案是:

nk=n!×(j=0nk(nj)(1)jG(n,nkj)j=0nk1(nj)(1)j G(n,nk1j))=n!×(j=0nk(nj)(1)jG(n,nkj)+j=1nk(nj1)(1)j1 G(n,nkj))=n!×j=0nk(n+1j)(1)j(nkj)nn!=j=0nk(n+1j)(1)j(nkj)n.

非常标准的卷积形式,一次卷积即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int MOD=998244353;
const int N=1<<20;
int ksm(int a,int b){
int ret=1;a%=MOD;
for(;b;b>>=1,a=(ll)a*a%MOD)if(b&1)ret=(ll)ret*a%MOD;
return ret;
}
int n,fact[N],inv_fact[N],f[N],g[N],r[N],w[N];
void ntt(int n,int *x,int op){
rep(i,0,n-1)if(r[i]<i)swap(x[i],x[r[i]]);
for(int i=1;i<n;i<<=1){
int gn=ksm(op==1?3:332748118,(MOD-1)/(i<<1));
w[0]=1;
for(int k=1;k<i;k++)w[k]=(ll)w[k-1]*gn%MOD;
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++){
int t1=x[j+k],t2=(ll)x[j+k+i]*w[k]%MOD;
x[j+k]=(t1+t2)%MOD;
x[j+k+i]=(t1-t2+MOD)%MOD;
}
}
if(op==-1){
int inv=ksm(n,MOD-2);
rep(i,0,n-1)x[i]=(ll)x[i]*inv%MOD;
}
}
int main(){
fastio;
cin>>n;
fact[0]=1;
rep(i,1,N-5)fact[i]=(ll)fact[i-1]*i%MOD;
inv_fact[N-5]=ksm(fact[N-5],MOD-2);
for(int i=N-5;i>=1;i--)inv_fact[i-1]=(ll)inv_fact[i]*i%MOD;
rep(i,0,n){
f[i]=(ll)fact[n+1]*inv_fact[i]%MOD*inv_fact[n+1-i]%MOD;
if(i&1)f[i]=(MOD-f[i])%MOD;
g[i]=ksm(i,n);
}
int p=1,deg=n+n;
while(p<=deg)p<<=1;
for(int i=0;i<p;i++)r[i]=(i&1)*(p>>1)+(r[i>>1]>>1);
ntt(p,f,1);ntt(p,g,1);
rep(i,0,p-1)f[i]=(ll)f[i]*g[i]%MOD;
ntt(p,f,-1);
rep(i,0,n)cout<<f[n-i]<<' ';
return 0;
}
posted @   yoshinow2001  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示