题解:AT_agc055_f [AGC055F] Creative Splitting

posted on 2025-02-07 13:27:53 | under | source

题意:对于一个 \(nm\) 的序列 \(a\),若可以将其剖分为 \(n\) 个长 \(m\) 的子序列,满足每个子序列第 \(i\)\(\le i\),则称它是合法的。分别假设 \(a_x=y\),求合法的 \(a\) 的数量。\(n,m\le 20\)

做法源自 WC2025 郭羽冲大蛇的讲解,和现有题解貌似不太一样?

典中典之寻找充要条件,从后往前贪心填写子序列,记 \(b_i\) 表示第 \(i\) 个子序列接下来填的位置,对于 \(a_i\) 找到可以容下它的最小的 \(b_j\)。容易反证法证明。

\(b\) 升序排列,特殊地 \(b_{n+1}=m\)。考虑通过 \(b\) 的变化反推 \(a\),具体来说,填写 \(a_i\) 时,选取一个 \(b_j\) 填进去,则 \(a_i\)\(b_{j+1}-b_j\) 种取值方案。

\(c=b_{i}-b_{i-1}\),那么等价于选取 \(j\) 然后产生 \(c_j\) 的贡献,并让 \(c_j-1\)\(c_{j+1}+1\)

注意到 \(-1+1\) 的过程可以用小球的移动来刻画,同时不同小球的选取又能体现 \(\times c\)。于是不考虑限制的情况下,一个合法 \(a\) 序列等价于下列过程:有 \(m\) 个小球,一开始位于 \(1\),每次可以选取一个向右移动一格,最后全移到 \(n+1\)\(c_i\) 就是位置 \(i\) 上的小球数量。

考虑 \(a_x=y\) 带来的限制。不妨分为两个阶段:第一阶段即前 \(nm-x\) 步、第二阶段即后 \(x\) 步。枚举 \(a_x\) 填到了 \(b_t\),那么要求第一阶段结束后 \(\sum\limits_{i<t} c_i<y\le \sum\limits_{i\le t} c_i\)。可以差分拆一下,只考虑右边这个限制即可。\(\sum\limits_{i\le t}c_i\) 又等价于前 \(t\) 个位置上的球数。

考虑通过球的移动刻画第 \(nm-x+1\) 步(即 \(a_x=y\)),你找到 \(y\) 对应的那个球在移动显然不现实,不妨移动编号最小的球,这样十分方便计数。

终于可以 dp 了!记 \(lim\) 为我们关心的前 \(lim\) 个位置(需要满足这些位置上的球数 \(\ge y\))。记 \(f_{i,j,k,st}\) 为考虑前 \(i\) 个球、第一阶段有 \(j\) 个操作、在第一阶段移动到的位置 \(\le lim\) 的球数量为 \(k\)\(st\) 用于处理第 \(nm-x+1\) 步,\(st=0\) 表示此前不存在在第一阶段移动了 \(t-1\) 步的球,即已经有小球被安排在 \(nm-x+1\) 步移动了,\(st=1\) 反之。

转移灰常容易,枚举当前球在第一阶段操作数即可。最后算答案就 \(ans_{x,y}\gets fl\times \sum\limits_{k\ge y}f_{m,nm-x,k,1}\)\(fl\) 是差分标记。

复杂度 \(O(n^3m^3)\)

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define ADD(a, b) a = (a + b) % mod
const int N = 25, NM = 405;
int n, m, mod, ans[NM][N], f[N][NM][N][2], c[NM][NM];

inline void solve(int t, int lim, int fl){
	memset(f, 0, sizeof f);
	f[0][0][0][0] = 1;
	for(int i = 1; i <= m; ++i){
		for(int j = 0; j <= (i - 1) * n; ++j)
			for(int k = 0; k < i; ++k){
				int j2 = (i - 1) * n - j;
				for(int p = 0; p <= n; ++p){
					if(p == t - 1){
						ADD(f[i][j + p][k + (p < lim)][1], f[i - 1][j][k][0] * c[j + p][p] % mod * c[j2 + n - p - 1][n - p - 1] % mod);	
						ADD(f[i][j + p][k + (p < lim)][1], f[i - 1][j][k][1] * c[j + p][p] % mod * c[j2 - 1 + n - p][n - p] % mod);
					}
					else{
						ADD(f[i][j + p][k + (p < lim)][0], f[i - 1][j][k][0] * c[j + p][p] % mod * c[j2 + n - p][n - p] % mod);
						ADD(f[i][j + p][k + (p < lim)][1], f[i - 1][j][k][1] * c[j + p][p] % mod * c[j2 - 1 + n - p][n - p] % mod);
					}
				}
			}
	}
	for(int x = 1; x <= n * m; ++x)
		for(int y = 1; y <= m; ++y)
			for(int k = y; k <= m; ++k)
				ADD(ans[x][y], fl * f[m][n * m - x][k][1] % mod);
}
signed main(){
	cin >> n >> m >> mod;
	c[0][0] = 1;
	for(int i = 1; i < NM; ++i){
		c[i][0] = c[i][i] = 1;
		for(int j = 1; j < i; ++j) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
	for(int t = 1; t <= n; ++t) solve(t, t, 1), solve(t, t - 1, mod - 1);
	for(int i = 1; i <= n * m; ++i){
		for(int j = 1; j <= m; ++j) printf("%lld ", ans[i][j]);
		putchar('\n');
	}
	return 0; 
}
posted @ 2026-01-15 08:13  Zwi  阅读(4)  评论(0)    收藏  举报