题解:AT_arc190_b [ARC190B] L Partition

题目传送门

很显然每次填完 L 之后所覆盖的图形为正方形,不然最最后无法填出正方形。现在假设我们已经确定了一个 \(k\) 阶的 L,要求它的方案数。

对于 \([1,k-1]\) 阶 L 的放法,每阶的 \(4\) 种方向都对应着一种方案,但 \(1\)\(4\) 种都是一样的,所以总方案数为 \(4^{k-2}\) 种。

对于 \([k+1,n]\) 阶 L 的放法,设其 \(k\) 阶 L 离上、下、左、右边界的距离分别为 \(a,b,c,d\),那么从中选出那些是覆盖上面的,那些是覆盖左边的,即可唯一确定一种放法,方案数 \(\binom{a+b}{a}\binom{c+d}{c}\)

枚举 \((a,b)\) 是在 L 的四个角上或四个方向的边上,角的情况下 L 是确定的,直接算就行了。边上的情况中 L 是可以上下或左右平移的,其式子就是组合数的前缀和。记 \(f(n,k)=\sum\limits_{i=0}^k\binom{n}{k}\)。显然可以从 \(f(n,k)\) 转移到 \(f(n,k-1)\)\(f(n,k+1)\)。又有

\[\binom{n}{k}=\binom{n-1}{k-1}+\binom{n-1}{k} \]

所以

\[f(n,k)=f(n-1,k-1)+f(n-1,k) \]

\[f(n-1,k)=\frac{f(n,k)+\binom{n-1}{k}}{2} \]

这样我们就能从 \(f(n,k)\) 转移到 \(f(n-1,k)\) 了。可以观察到总移动次数是 \(O(n)\) 的,直接转移即可。

一些注意的点:

  • 角的方案要 \(\times 3\),边的方案要 \(\times 2\)
  • 转移的过程中可能会转移到 \(f(n,k)\)\(k>n\) 的情况,这时候要从 \(f(n,n)=2^n\) 去转移。

Code:

const int N = 2e7 + 10, MOD = 998244353;
int n, a, b, q;
long long jc[N], jny[N], ny[N], pw2[N];
vector < pair <int, int> > vt, pt;
long long C(int x, int y){
	return jc[y] * jny[x] % MOD * jny[y - x] % MOD; 
}
long long gt(int x, int k){
	if(x < 0)
		return 0;
	auto t = (x < k / 2)? make_pair(0, 1ll) : make_pair(k, pw2[k]);
	for(auto i : vt)
		if(i.first <= k && abs(t.first - x) > abs(i.first - x))
			t = i;
	while(t.first < x)
		t.first++, t.second = (t.second + C(t.first, k)) % MOD;
	while(t.first > x)
		t.second = (t.second - C(t.first, k) + MOD) % MOD, t.first--;
	pt.push_back(t);
	return t.second;
}
long long calc(int a, int b, int c, int d){
	if(a <= 0 || b <= 0 || c > n || d > n)
		return 0;
	return C(a - 1, a - 1 + n - c) * C(b - 1, b - 1 + n - d) % MOD;
}
long long calc(int x, int y, int k){
	int l = max(1, y - k + 2), r = min(n - k + 1, y - 1);
	if(l > r)
		return 0;
	long long t = (gt(r - 1, n - k) - gt(l - 2, n - k) + MOD) % MOD, sum = 0;
	if(x + k - 1 <= n)
		sum = (sum + t * C(x - 1, n - k)) % MOD;
	if(x - k + 1 > 0)
		sum = (sum + t * C(n - x, n - k)) % MOD;
	return sum;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> a >> b >> q;
	jc[0] = jc[1] = ny[0] = ny[1] = jny[0] = jny[1] = pw2[0] = 1, pw2[1] = 2;
	for(int i = 2; i <= 2 * n; i++)
		ny[i] = (MOD - MOD / i) * ny[MOD % i] % MOD, jc[i] = jc[i - 1] * i % MOD, jny[i] = jny[i - 1] * ny[i] % MOD, pw2[i] = pw2[i - 1] * 2 % MOD;
	int la = n;
	while(q--){
		int k;
		cin >> k;
		while(la > n - k){
			la--;
			for(auto &i : vt)
				i.second = (i.second + C(i.first, la)) % MOD * ny[2] % MOD;
		}
		pt.clear();
		long long sum = (calc(a, b, k) + calc(b, a, k)) * 2 % MOD;
		if(k > 1)
			sum = (sum +(calc(a - k + 1, b - k + 1, a, b) + calc(a - k + 1, b, a, b + k - 1) + calc(a, b - k + 1, a + k - 1, b) + calc(a, b, a + k - 1, b + k - 1)) * 3) % MOD;
		else
			sum = (sum + calc(a, b, a, b)) % MOD;
		swap(pt, vt);
		sort(vt.begin(), vt.end());
		vt.erase(unique(vt.begin(), vt.end()), vt.end());
		cout << sum * pw2[max(0, k - 2) * 2] % MOD << "\n";
	}
}
posted @ 2025-01-15 23:19  louisliang  阅读(34)  评论(0)    收藏  举报