ARC162F Montage

脑子被吃掉了。

手玩一下,容易转化题意为:

按行从上到下填 \(0/1\) 矩阵,设第 \(i\)非空行上是 \(1\) 的位置的集合为 \(S_i\),满足:

  • 对于任意 \(i>1\),令 \(D=S_i\cup S_{i-1}\)
  • \(D=\varnothing\),则 \(S_i\) 中所有元素均比 \(S_{i-1}\) 中任意元素小,即 \(\max\limits_{i\in S_i}i<\min\limits_{i\in S_{i-1}}i\)
  • \(D\neq\varnothing\),则 \(S_i,S_{i-1},D\) 从小到大排序后,\(D\)\(S_i\) 的后缀,为 \(S_{i-1}\) 的前缀,即 \(\max\limits_{i\in S_i\setminus D}i<\min\limits_{i\in D}i\le \max\limits_{i\in D}i<\min\limits_{i\in S_{i-1}\setminus D}i\)

现在这个限制是从右上角走到左下角,那不如先把行翻转一下,就变成了从左上角到右下角:

  • \(D=\varnothing\)\(\min\limits_{i\in S_i}i>\max\limits_{i\in S_{i-1}}i\)
  • \(D\neq\varnothing\)\(\min\limits_{i\in S_i\setminus D}i>\max\limits_{i\in D}i\ge \min\limits_{i\in D}i>\max\limits_{i\in S_{i-1}\setminus D}i\)

那我们直接把空行删掉,令 \(f_{i,j,k}\) 表示考虑到第 \(i\) 行,第 \(i\) 行一共有 \(j\)\(1\),最右边的 \(1\) 的位置为 \(k\) 的方案数。转移枚举新的最右边的 \(1\) 的位置 \(p\)\(D\) 的大小 \(s\)\(S_{i+1}\setminus D\) 的大小 \(l\),转移系数就是个组合数:

\[f_{i,j,k}\dbinom{p-k-1}{s-1}\to f_{i+1,l+s,p} \]

枚举非空的行数,答案就是:

\[\text{ans}=1+\sum\limits_{i=1}^{n}\dbinom{n}{i}\sum\limits_{j=1}^m\sum\limits_{k=1}^{m-j+1}f_{i,j,k} \]

直接做是 \(O(n^6)\) 的。利用不同的 \(l\) 转移相同可用前缀和优化至 \(O(n^5)\)

然后我们发现把矩阵转置一下,将 \(n,m\) 交换不影响答案,也就是说我们可以把空的列也拿出来。于是每个 \(S_i\) 就是一段区间了,且 \(S_i,S_{i+1}\) 要么相交不包含要么相邻,\(S_{i+1}\)\(S_i\) 的右边。

\(f_{i,j,k}\) 重新定义为 \(S_1=[1,r_1],S_i=[j-k+1,j]\),满足以上限制的方案数。其实就是把前面的 \(j,k\) 两维交换了一下。

那么答案变为:

\[\text{ans}=1+\sum\limits_{i=1}^n\sum\limits_{j=1}^m\dbinom{n}{i}\dbinom{m}{j}\sum\limits_{k=1}^jf_{i,j,k} \]

转移还是枚举 \(S_{i+1}\) 的两个端点 \(p,q\),满足 \(j-k+1\le p\le j+1,\max(p,j)\le q\le m\):

\[f_{i,j,k}\to f_{i+1,q,q-p+1} \]

然而这还是 \(O(n^5)\) 的。由于对于不同的 \(p\) 贡献相同,设 \(f'_{i,j,k}\) 为第三维差分后的 \(f\) 数组,可用前缀和优化至 \(O(n^4)\)

\[f_{i,j,k}\to f'_{i+1,q,q-\min(j+1,q)+1},-f_{i,j,k}\to f'_{i+1,q,q-j+k+1} \]

单独将 \(q=j\) 的转移拎出来,这部分 \(O(n^3)\)

\[f_{i,j,k}\to f'_{i+1,j,1},-f_{i,j,k}\to f'_{i+1,j,k+1} \]

剩下的是 \(q\ge j+1\) 的转移:

\[f_{i,j,k}\to f'_{i+1,q,q-j},-f_{i,j,k}\to f'_{i+1,q,q-j+k+1} \]

这还是 \(O(n^4)\) 的,但是我们观察到第二维和第三维的差为定值且与 \(q\) 无关,所以令 \(g_{i,j,k+1}=f'_{i,j,j-k}\)

\[f_{i,j,k}\to g_{i+1,q,j+1},-f_{i,j,k}\to g_{i+1,q,j-k} \]

注意到 \(q\in [j+1,m]\),再对 \(g\) 的第二维进行差分记作 \(g'\),前缀和优化即可做到 \(O(n^3)\)

\[f_{i,j,k}\to g'_{i+1,j+1,j+1},-f_{i,j,k}\to g'_{i+1,j+1,j-k} \]

最终时间复杂度 \(O(n^3)\),空间复杂度 \(O(n^2)\)

#include <bits/stdc++.h>
#define eb emplace_back
#define pb pop_back
#define mt make_tuple
#define mp make_pair
#define fi first
#define se second

using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tu;
bool Mbe;


const int N = 405;
const int P = 998244353;

int n, m, ans, f[2][N][N], g[2][N][N], C[N][N];

int qpow(int p, int q) {
	int res = 1;
	for (; q; q >>= 1, p = 1ll * p * p % P)
		if (q & 1) res = 1ll * res * p % P;
	return res;
}

void init(int lim) {
	C[0][0] = 1;
	for (int i = 1; i <= lim; i++) {
		C[i][0] = 1;
		for (int j = 1; j <= lim; j++)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
	}
}

void solve() {
	cin >> n >> m, init(400);
	for (int i = 1; i <= m; i++) 
			f[1][i][i] = 1;
	for (int i = 1, t = 1; i <= n; i++, t ^= 1) {
		memset(f[t ^ 1], 0, sizeof(f[t ^ 1]));
		memset(g[t ^ 1], 0, sizeof(g[t ^ 1]));
		for (int j = 1; j <= m; j++) {
			for (int k = 1; k <= j; k++)
				(g[t][j][k] += g[t][j - 1][k]) %= P;
			for (int k = 1; k <= j; k++)
				(f[t][j][k] += g[t][j][j - k + 1]) %= P;
			for (int k = 1; k <= j; k++)
				(f[t][j][k] += f[t][j][k - 1]) %= P;
			for (int k = 1; k <= j; k++) {
				(f[t ^ 1][j][1] += f[t][j][k]) %= P;
				(f[t ^ 1][j][k + 1] += P - f[t][j][k]) %= P;
				(g[t ^ 1][j + 1][j + 1] += f[t][j][k]) %= P;
				(g[t ^ 1][j + 1][j - k] += P - f[t][j][k]) %= P;
			}
			int res = 0;
			for (int k = 1; k <= j; k++) 
				(res += f[t][j][k]) %= P;
			(ans += 1ll * C[n][i] * C[m][j] % P * res % P) %= P;
		}
	}
	cout << (ans + 1) % P << '\n';
}

bool Med;
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cerr << (&Mbe - &Med) / 1048576.0 << " MB\n";
	#ifdef FILE
		freopen("1.in", "r", stdin);
		freopen("1.out", "w", stdout);
	#endif
	int T = 1;
	// cin >> T;
	while (T--) solve();
	cerr << (int)(1e3 * clock() / CLOCKS_PER_SEC) << " ms\n";
	return 0;
}
posted @ 2024-01-17 19:15  Ender_32k  阅读(5)  评论(0编辑  收藏  举报