[atAGC055F]Creative Splitting
考虑判定序列$\{b_{i}\}$是否"amazing"——
维护$n$个序列的剩余长度,从后往前枚举$b_{i}$,问题转换为以下模型:
对于长为$n$的序列$\{c_{j}\}$(初始均为$k$),每次选择$c_{j}\ge b_{i}$减1,要求存在一种合法方案
事实上,可以贪心选择最小的$c_{j}$减1,证明如下:
引理:对于存在合法方案的$\{c_{j}\}$,贪心操作一次后仍存在
假设贪心操作$x$而合法方案操作$y$,若$c_{x}=c_{y}$即得证,进而有$c_{x}<c_{y}$
不妨强制该方案在$c_{x}=c_{y}$时不操作$y$(操作$x$等价),即恒有$c_{x}\le c_{y}$
构造:操作$x$后模仿该方案操作,仅将下一次操作$x$改为操作$y$
关于构造的合法性,分为三部分:
1.下一次操作$x$之前,注意到仅有$c_{x}$较小,显然成立
2.下一次操作$x$时,由于$c_{x}\le c_{y}$,改为操作$c_{y}$也合法
3.下一次操作$x$之后,两者$\{c_{j}\}$均相同,显然成立
综上,根据引理归纳即得证
记$c'_{i}=\sum_{j=1}^{n}[c_{j}<i]$,则操作时对应的影响即将$c'_{b_{i}}$加1并重新排序(可以自行分析)
在此基础上,结合$b_{pos}=val$的限制,问题又转换为以下模型:
对于长为$k$的序列$\{c'_{i}\}$(初始均为$0$),每次将$c'_{b_{i}}\ne n$加1并重新排序,求$b_{pos}=val$的方案数
在$\{b_{i}\}$无限制时,重新排序并没有意义,进而方案数可以用组合数得到
对于$b_{pos}=val$的限制,考虑枚举在$i=pos$处排序后的$\{c'_{i}\}$,则答案即
$$
\sum_{\begin{array}{}0\le c'_{1}\le c'_{2}\le ...\le c'_{k}\le n\\\sum_{i=1}^{k}c'_{i}=nk-pos\end{array}}P(\{c'_{i}\}){nk-pos\choose c'_{1},c'_{2},...,c'_{k}}{pos-1\choose n-c'_{1},n-c'_{2},...,n-c'_{val}-1,...,n-c'_{k}}
$$
(其中$P(\{c'_{i}\})$表示$\{c'_{i}\}$任意排列所能得到的不同序列数)
仅需对每一个$val$进行一次dp,单次dp复杂度为$o(n^{2}k^{3})$,总复杂度为$o(n^{2}k^{4})$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 30 4 #define M 500 5 #define ll long long 6 int n,m,mod,fac[M],inv[N],g[M],f[N][N][M],ans[M][N]; 7 int main(){ 8 scanf("%d%d%d",&n,&m,&mod); 9 fac[0]=inv[0]=inv[1]=1; 10 for(int i=1;i<M;i++)fac[i]=(ll)fac[i-1]*i%mod; 11 for(int i=2;i<N;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; 12 for(int i=1;i<N;i++)inv[i]=(ll)inv[i-1]*inv[i]%mod; 13 for(int val=1;val<=m;val++){ 14 memset(f,0,sizeof(f)); 15 for(int i=1;i<=m;i++){ 16 memset(g,0,sizeof(g)); 17 if (i==1)g[0]=1; 18 for(int j=0;j<=n;j++){ 19 for(int k=0;k<=n*m;k++){ 20 if (!g[k])continue; 21 int k0=k,s=g[k]; 22 for(int ii=i;ii<=m;ii++){ 23 if ((ii==val)&&(j==n))break; 24 k0+=j,s=(ll)s*inv[j]%mod*inv[n-j-(ii==val)]%mod; 25 f[ii][j][k0]=(f[ii][j][k0]+(ll)s*inv[ii-i+1])%mod; 26 } 27 } 28 for(int k=0;k<=n*m;k++)g[k]=(g[k]+f[i-1][j][k])%mod; 29 } 30 } 31 for(int pos=1;pos<=n*m;pos++){ 32 for(int i=0;i<=n;i++)ans[pos][val]=(ans[pos][val]+f[m][i][n*m-pos])%mod; 33 ans[pos][val]=(ll)ans[pos][val]*fac[m]%mod*fac[n*m-pos]%mod*fac[pos-1]%mod; 34 } 35 } 36 for(int pos=1;pos<=n*m;pos++){ 37 for(int val=1;val<=m;val++)printf("%d ",ans[pos][val]); 38 printf("\n"); 39 } 40 return 0; 41 }