题解:AT_arc190_b [ARC190B] L Partition

题目传送门

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

对于 [1,k1] 阶 L 的放法,每阶的 4 种方向都对应着一种方案,但 14 种都是一样的,所以总方案数为 4k2 种。

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

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

(nk)=(n1k1)+(n1k)

所以

f(n,k)=f(n1,k1)+f(n1,k)

f(n1,k)=f(n,k)+(n1k)2

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

一些注意的点:

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

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"; } }

__EOF__

本文作者louisliang
本文链接https://www.cnblogs.com/louisliang/p/18673893.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   louisliang  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示