先考虑 \(q=(1...n)\) 的情况:
发现如果设 \(divcnt(p)\) 表示将 \(p\) 划分为极小值域连续段的个数,那满足 \(divcnt(p)\ge m\) 的排列都是合法的。
那现在要求出有多少个排列符合条件
可以先算出长度为 \(i\) ,\(divnct\) 为 \(1\) 的排列个数,这可以用dp解决
然后再背包一下,就能求出符合条件的排列数
其他情况:
经过证明,我们必然可以将 \(q\) 拆解为三段(第三段可以没有):
1.(极长)单调递增段
2.断掉之后,又一个单调递增段(不用极长),且满足除第一个数之外,其他数都比第一段末尾大,并且 \(q_{t_1+1}=p_{t_2}\)
3.单点段
由于第一段末尾可求,那我们只需要枚举第二段末尾的位置,这样就能求出第一段需要被划分的段数,用第一种情况求法即可,然后第二段除了第一个数,其他数乱排就好了
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 505
int n,m,ans,t;
const int mod=998244353;
int a[N],f[N][N],q[N],jc[N];
int cal(int tt){
int c=m-(n-tt)-1;
if(c<0) return 0;
return f[c][t]*jc[tt-t-1]%mod;
}
void mo(int &x){
if(x>=mod) x-=mod;
}
signed main(){
//freopen("data.in","r",stdin);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&q[i]);
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
for(int i=1;i<=n;i++){
a[i]=jc[i];
for(int j=1;j<i;j++) a[i]+=mod-a[j]*jc[i-j]%mod,mo(a[i]);
}
for(int i=1;i<=n;i++) f[1][i]=a[i];
for(int i=2;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<j;k++) f[i][j]+=f[i-1][j-k]*a[k]%mod,mo(f[i][j]);
}
}
for(int i=1;i<=n;i++){
if(q[i]>q[i-1]) t=i;
else break;
}
if(t==n){
for(int i=m;i<=n;i++) ans+=f[i][n],mo(ans);
printf("%lld\n",ans);
return 0;
}
ans=cal(t+1);
for(int i=t+2;i<=n;i++){
if(q[i]<q[t]) break;
if(q[i]<q[i-1]) break;
ans+=cal(i),mo(ans);
}
printf("%lld\n",ans);
return 0;
}
标签:2024.2,排列,17,int,题解,第一段,末尾,mod
From: https://www.cnblogs.com/hubingshan/p/18018105