2024.2.17模拟赛T1题解

先考虑 \(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;
}

posted @ 2024-02-17 16:32  hubingshan  阅读(37)  评论(0)    收藏  举报