Description
- n ≤ 1000 n\le1000 n≤1000
Solution
- 梅开二度,几个月前才做过相同套路的题目,结果由于对于容斥的理解不够透彻,最后没有想清楚乱推出来的东西的正确性。
- 首先可以发现一个结论,对于一个排列,找到最长长度为 l e n len len的上升区间,它需要操作的次数是 n − l e n n-len n−len。
- 那么容易转化为对于每一个 k k k,求 m a x l e n ≤ k maxlen\le k maxlen≤k的排列的个数。
- 直接模拟DP可以做到 O ( n 4 ) O(n^4) O(n4)或 O ( n 5 ) O(n^5) O(n5)
- 一个很 n a i v e naive naive的做法是往当前的排列里面接一些连续上升的段,再直接乘上分配的组合数。
- 但是显然无法保证头尾会不会连接成更大的段,所以我们对于每一个长度为 l e n len len的段都要给它一个容斥系数 f ( l e n ) f(len) f(len)使得,在分配完标号以后如果组合成长度大于 k k k的段,贡献就是 0 0 0。
- 写成生成函数的形式, F ( x ) F(x) F(x)为 f f f的生成函数,即
∑ i = 0 i n f F i ( x ) = ∑ i = 1 k x i \sum_{i=0}^{inf}F^i(x)=\sum_{i=1}^kx^i i=0∑infFi(x)=i=1∑kxi
1 + ∑ i = 0 i n f F i ( x ) = ∑ i = 0 k x i 1+\sum_{i=0}^{inf}F^i(x)=\sum_{i=0}^kx^i 1+i=0∑infFi(x)=i=0∑kxi
1 1 − F ( x ) = x k + 1 − 1 x − 1 \frac{1}{1-F(x)}=\frac{x^{k+1}-1}{x-1} 1−F(x)1=x−1xk+1−1
F ( x ) = 1 − x − 1 x k + 1 − 1 F(x)=1-\frac{x-1}{x^{k+1}-1} F(x)=1−xk+1−1x−1
F ( x ) = 1 + ( x − 1 ) 1 1 − x k + 1 F(x)=1+(x-1)\frac{1}{1-x^{k+1}} F(x)=1+(x−1)1−xk+11
F ( x ) = ∑ i ≥ 0 x ( k + 1 ) i + 1 − ∑ i ≥ 1 x ( k + 1 ) i F(x)=\sum_{i\ge0}x^{(k+1)i+1}-\sum_{i\ge1}x^{(k+1)i} F(x)=i≥0∑x(k+1)i+1−i≥1∑x(k+1)i
- 由于转移函数 F ( x ) F(x) F(x)只有 n k \frac{n}{k} kn项,所以暴力卷积,复杂度是 O ( n ∗ n k ) O(n*\frac{n}{k}) O(n∗kn)。
- 总复杂度就是 O ( n 2 l o g n ) O(n^2log\ n) O(n2log n)的。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 1005
#define ll long long
using namespace std;
int n,mo,i,j,k;
ll fct[maxn],invf[maxn],ans[maxn],f[maxn];
ll ksm(ll x,ll y){
ll s=1;
for(;y;y/=2,x=x*x%mo) if (y&1)
s=s*x%mo;
return s;
}
int main(){
// freopen("ceshi.in","r",stdin);
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
scanf("%d%d",&n,&mo);
fct[0]=1;for(i=1;i<maxn;i++) fct[i]=fct[i-1]*i%mo;
invf[maxn-1]=ksm(fct[maxn-1],mo-2);
for(i=maxn-2;i>=0;i--) invf[i]=invf[i+1]*(i+1)%mo;
for(k=1;k<n;k++){
memset(f,0,sizeof(f));
f[0]=1;
for(i=0;i<n;i++) {
for(j=0;i+(k+1)*j+1<=n;j++)
(f[i+(k+1)*j+1]+=f[i]*invf[(k+1)*j+1])%=mo;
for(j=1;i+(k+1)*j<=n;j++)
(f[i+(k+1)*j]-=f[i]*invf[(k+1)*j])%=mo;
}
ans[k]=f[n]*fct[n]%mo;
}
ans[n]=fct[n]; ll s=0;
for(i=0;i<n;i++){
(s+=ans[n-i]-ans[n-i-1])%=mo;
printf("%lld\n",(s+mo)%mo);
}
}