problem
将正整数 1∼n1\sim n1∼n 任意划分成 mmm 个非空集合 A1,...,AmA_1,...,A_mA1,...,Am。
一个划分是守序的,当且仅当存在一个环排列 (p1,...,pm)(p_1,...,p_m)(p1,...,pm),使得 maxApi>minApi−1\max A_{p_i}>\min A_{p_{i-1}}maxApi>minApi−1。p0=pmp_0=p_mp0=pm。
两个划分本质不同,当且仅当存在两个数在一个划分中属于同一个集合,而在另一个划分成属于不同集合。
求本质不同的守序划分方案数,对 998244353
取模。
n,m≤500n,m\le 500n,m≤500。
solution
守序的判定可以转化为:不存在一个 iii,使得 1∼i1\sim i1∼i 各自隶属集合的集合 S⋂S\bigcapS⋂ i+1∼ni+1\sim ni+1∼n 各自隶属集合的集合 T=∅T=\emptyT=∅。
简单证明一下:不管圆排列是怎样的,TTT 里面的集合最小值最大值都是 ≥i+1\ge i+1≥i+1,而 SSS 里面的集合最小值最大值都是 ≤i\le i≤i 的,而圆排列至少会让一个属于 SSS 的集合在一个属于 TTT 的集合后一个位置,那么这个时候一定无法满足条件。
设 f(i,j,k):f(i,j,k):f(i,j,k): 考虑前 iii 个数,一共划分成了 jjj 个集合,其中有 kkk 个集合还未封闭。区间封闭代表这已经生成了一个集合,之后不会再加数了。
则除了 i=ni=ni=n 时,其余时候是不能 k=0k=0k=0 的。考虑转移到 f(i,j,k)f(i,j,k)f(i,j,k) 的几种情况。
- 新增一个封闭区间。f(i−1,j−1,k)f(i-1,j-1,k)f(i−1,j−1,k)。
- 新增一个未封闭区间。f(i−1,j−1,k−1)f(i-1,j-1,k-1)f(i−1,j−1,k−1)。
- 随便加入一个未封闭区间后仍处于未封闭状态。f(i−1,j,k)×kf(i-1,j,k)\times kf(i−1,j,k)×k。
- 随便加入一个未封闭区间后使之封闭。f(i−1,j,k+1)×(k+1)f(i-1,j,k+1)\times (k+1)f(i−1,j,k+1)×(k+1)。
code
#include <bits/stdc++.h>
using namespace std;
#define mod 998244353
#define maxn 505
int dp[maxn][maxn][maxn];
int n, m;signed main() {scanf( "%d %d", &n, &m );dp[0][0][0] = 1;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )for( int k = ( i != n );k <= j;k ++ ) //在n之前是不能让集合出现全都封闭的情况dp[i][j][k] = ( 1ll * dp[i - 1][j - 1][k] + 1ll * dp[i - 1][j - 1][max( 0, k - 1 )] + 1ll * dp[i - 1][j][k + 1] * ( k + 1 ) + 1ll * dp[i - 1][j][k] * k ) % mod;//新开一个封闭集合 / 自己单独为一个集合//新开一个不封闭的集合等待后续的加入//随便加入一个不封闭的集合使之封闭//随便加入一个不封闭的集合等待后续加入printf( "%d\n", dp[n][m][0] );return 0;
}