CF GYM 102956B


题目概述:

询问有多少个长度为 \(n\),值域在 \([1,m]\) 内的序列满足对于 \(\forall i \in[1,n-1]\) 不存在 \(\max(a_1...a_i)=\min(a_{i+1}...a_n)\)

数据范围:

\(n \leq 400,m\leq 10^9\),模数是质数

题解:

考虑 \(\text{DP}\)。 观察到 \(m\) 很大,那么显然不能把 \(m\) 直接扔进状态,所以考虑计算出 \(g_{k}\) 表示恰好有 \(k\) 个不同值的序列,就可以乘一个组合数算出答案了。但 \(g\) 数组也不能直接计算,那试试二项式反演。设 \(f_{i,k}\) 表示长度为 \(i\) ,值域在 \([1,k]\) 内有多少种合法序列 (换句话说,出现了最多 \(k\) 种不同的值)

\(f_{i,k}\) 可以通过容斥,枚举最后一个不合法的位置在哪里快速计算出来,式子长成

\[f_{i,k}=k^i-\sum_{j=1}^{i-1}\sum_{h=1}^{k}(h_j-(h-1)^j)(f_{i-j,k-h+1}-f_{i-j,k-h}) \]

直接计算,复杂度 \(O(n^4)\) ,考虑前缀和优化,即把式子后面拆成

\[\sum_{j=1}^{i-1}\sum_{h=1}^{k}h^jf_{i-j,k-h+1}+(h-1)^jf_{i-j,k-h}-h^jf_{i-j,k-h}-(h-1)^jf_{i-j,k-h+1} \]

分成四部分分别前缀和优化即可。最后二项式反演一下就可以直接算答案了。

\[g_i=f_i-\sum_{k=1}^{i-1}\binom{i}{k}g_k \]

如果有什么拆的更少做法欢迎教导我。

const int N=405;
int n,m,mod;
int f[N][N],g[N],pw[N][N],sa[2][N][N],sb[2][N][N],sc[2][N][N],sd[2][N][N];
int fac[N],ifac[N];
int add(int x,int y){
	if(x+y>=mod)return x+y-mod;
	return x+y;
}
int del(int x,int y){
	if(x-y<0)return x-y+mod;
	return x-y;
}
int ksm(int x,int k){
	int base=1;
	while(k){
		if(k&1)base=1ll*base*x%mod;
		x=1ll*x*x%mod;k>>=1;
	}
	return base;
}
int C(int n,int m){
	if(n=1;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
	for(int i=1;i<=n;i++){
		for(int k=1;k<=n;k++){
			for(int h=1;h<=k;h++){
				f[i][k]=add(f[i][k],sb[(i-1)&1][k][h]);
				f[i][k]=add(f[i][k],sc[(i-1)&1][k][h]);
				f[i][k]=del(f[i][k],sa[(i-1)&1][k][h]);
				f[i][k]=del(f[i][k],sd[(i-1)&1][k][h]);
			}
			f[i][k]=del(pw[k][i],f[i][k]);
			for(int h=1;h<=k;h++){
				sa[i&1][k][h]=add(1ll*sa[(i-1)&1][k][h]*h%mod,1ll*f[i][k-h]*h%mod);
				sb[i&1][k][h]=add(1ll*sb[(i-1)&1][k][h]*h%mod,1ll*f[i][k-h+1]*h%mod);
				sc[i&1][k][h]=add(1ll*sc[(i-1)&1][k][h]*(h-1)%mod,1ll*f[i][k-h]*(h-1)%mod);
				sd[i&1][k][h]=add(1ll*sd[(i-1)&1][k][h]*(h-1)%mod,1ll*f[i][k-h+1]*(h-1)%mod);
			}
		}
	}
	for(int i=1;i<=n;i++){
		g[i]=f[n][i];
		for(int k=1;km)break;
		int fz=1;
		for(int k=m;k>=m-i+1;k--)fz=1ll*fz*k%mod;
		int tmp=1ll*fz*ifac[i]%mod;
		ans=add(ans,1ll*tmp*g[i]%mod);
	}
	cout<