Loading

CF1349F1 Slime and Sequences (Easy Version)

CF1349F1 Slime and Sequences (Easy Version)

好神仙啊。。。

我们可以构造出排列和好序列的双射,然后就做完了。

看样例发现输出总和是 \(n\cdot n!\) 就可以往这个结论猜 ,然而我太菜了,没构造出来/kk

把排列划分为若干下降的子区间,第 \(i\) 段子区间赋值为 \(i\) ,这样得到的序列一定可行。

比如

\[\mathrm{7,5,4,6,1,3,2}\to\mathrm{(7,5,4),(6,1),(3,2)}\to 2,3,3,1,1,2,1 \]

从一个好区间也可以还原出唯一确定的排列,所以就双射了。

从原本的数好序列可以转化为:数所有排列第 \(k\) 段长度总和。

考虑计算排列里位置 \(i\) 对于答案第 \(k\) 项的贡献。

那么 \(i\) 之前就有 \(k-1\) 个升高,方案数是 \(\left<\begin{matrix}i\\k-1\end{matrix}\right>\) (欧拉数相关内容可以看 多项式笔记(二)

\(i\) 之后随便排,是 \((n-i)!\)

同时我们还要选择 \(i\) 个数放到前面 \(i\) 项,是 \(\dbinom{n}{i}\)

整合起来就是

\[ans_k=\sum_{i=1}^{n}\left<\begin{matrix}i\\k-1\end{matrix}\right>(n-i)!\binom{n}{i} \]

\(O(n^2)\) 递推欧拉数然后直接算就好了。

#define mod 998244353
inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
const int N=5005;
int o[N][N],n,ans[N],fac[N],ifc[N];
inline int comb(int n,int m){return n<m?0:1ll*fac[n]*ifc[m]%mod*ifc[n-m]%mod;}
signed main(){
	n=read();
	o[0][0]=1;
	rep(i,1,n)rep(j,0,i-1){
		o[i][j]=1ll*o[i-1][j]*(j+1)%mod;
		if(j>0)o[i][j]=(1ll*o[i-1][j-1]*(i-j)%mod+o[i][j])%mod;
	}
	fac[0]=1;rep(i,1,n)fac[i]=1ll*fac[i-1]*i%mod;
	ifc[n]=qpow(fac[n],mod-2);per(i,n-1,0)ifc[i]=1ll*(i+1)*ifc[i+1]%mod;
	rep(k,1,n)rep(i,1,n)ans[k]=(ans[k]+1ll*fac[n-i]*o[i][k-1]%mod*comb(n,i)%mod)%mod;
	rep(i,1,n)printf("%d%c",ans[i]," \n"[i==n]);
	return 0;
}
posted @ 2021-01-21 19:09  zzctommy  阅读(146)  评论(0编辑  收藏  举报