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<