传送门
题解:
用学校的电脑只跑了43s。
发现当我们确定哪些位置下降后,把序列分成若干段,对于每段,我们确定哪些数放进去之后肯定就只有唯一方式了。
考虑枚举 ⌊ n k ⌋ \lfloor\frac{n}{k}\rfloor ⌊kn⌋个列里面有有哪些是强制上升,剩下的任意。
假设分出来的块大小为 a i a_i ai,则一行方案数为 n ! ∏ a i ! \frac{n!}{\prod a_i!} ∏ai!n!
考虑DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示以 i k ik ik结尾的,前面划分了 j j j块的方案数,显然有转移:
f [ i ] [ j ] = ∑ k = 0 i − 1 f [ k ] [ j − 1 ] l e n ! r f[i][j]=\sum_{k=0}^{i-1}\frac{f[k][j-1]}{len!^r} f[i][j]=k=0∑i−1len!rf[k][j−1]
其中 l e n len len是这次划分的块长。
第二位可以直接和容斥系数压在一起,单次DP复杂度 O ( ( n k ) 2 ) O((\frac{n}{k})^2) O((kn)2)
总复杂度积个分知道是 O ( n 2 ) O(n^2) O(n2)。
有一种生成函数做法,需要MTT,不想写,但是可以用Karatsuba优化到 O ( n log 2 3 ) O(n^{\log_23}) O(nlog23)
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int mod=1e9+123;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
cs int N=5e4+7;
int n=5e4,m;
int fac[N],ifac[N],b[N],f[N];
inline void init_inv(){
fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(int re i=2;i<=n;++i)fac[i]=mul(fac[i-1],i);
ifac[n]=power(fac[n],mod-2);
for(int re i=n-1;~i;--i)ifac[i]=mul(ifac[i+1],i+1);
for(int re i=2;i<=n;++i)
fac[i]=power(fac[i],n),ifac[i]=power(ifac[i],n);
}
inline int calc(){
f[0]=fac[n];
memset(f+1,0,sizeof(int)*m);
for(int re i=1;i<=m;++i){
for(int re j=i-1,l=0;~j;--j){
l+=b[j+1];
(i^j)&1?Inc(f[i],mul(f[j],ifac[l])):Dec(f[i],mul(f[j],ifac[l]));
}
}
return f[m];
}
signed main(){
init_inv();int ans=0;
for(int re i=1;i<=n;++i){m=0;
for(int re j=n;j;j-=b[m])b[++m]=std::min(i,j);
Inc(ans,calc());
}
std::cout<<ans<<"\n";
return 0;
}