题解:P14435 [JOISC 2013] 收拾吉祥物 / Mascots

news/2025/11/18 21:00:57/文章来源:https://www.cnblogs.com/Noivelist/p/19239312

\(\displaystyle{\large\textcolor{#00A0DE}{蓝是水的颜色}}\),所以我们来水一下这道蓝题的题解。

题目传送门

题意理解

我们首先给出一个 \(R\times C\) 的方格图,在其中任意 \(n\) 坐标中初始放着物品,我们依次选定坐标放入物品,使这个过程中出现有且仅有一个矩形的次数最多。

在锁定这个最多次数以后,我们需要求出满足这个次数的方案个数。

题意分析

\(\mathcal {Step\ 1}\):贪心规划放点策略

首先我们要使矩形次数最多,先要采取贪心的策略。对于初始化给出的散点,我们要让他们变成有且仅有一个的矩形还不够,为了我们后续的健康,所以我们要求出一个包裹这些散点的最小矩形凸壳,这是显而易见的。

接下来对于这个小矩形,我们要对他进行拓展,拓展到一个大小为 \(R\times C\) 的大矩形。这时要进行矩形出现次数最大化,我们贪心地想,每次选取这个小矩形的一条边进行扩展,扩展出一个 长或宽只差 1 的矩形,就成为了最优解。

对于这个策略可以反证法来证明,在这里我们只举一个反例就可以理解了,我们现在矩形的右边扩展边填满部分,再将上面的扩展边填充部分,最后结果向斜向上扩展了一层,但是小确幸却只增加了 \(1\) ,得不偿失。我们要是先专心扩展最右边,再扩展最上面,就得到了 \(2\) 的小确幸。

\(\mathcal {Step\ 2}\):多重集排列求解方案个数

这里是重中之重!

一、 DP

我们先割裂地看这个问题,不去考虑这个矩形的位置,先看它的形状,我们不难发现我们可以只考虑两种操作:纵向和横向操作,二元的操作比四元的操作简单太多了,并且确实只需要横或纵的扩展就可以达到 \(R\times C\)

接着我们把这个从 \(h\times w\) 扩展到 \(R\times C\) 的总问题扩展到若干个子问题,发现每个子问题之间是差不多无后效性的,二维表研究组合方案个数?这个好耳熟,不就是 \(\operatorname {DP(Dynamic\ Programming)}\) 吗?为什么要把它的全名写出来,因为 \(“Dynamic”\) 为动态, \(“Programming”\) 指一种表格法。

我们锁定这个原矩形的右上端点在新坐标系的 \((0,0)\) 处,我们要扩展到 \((H,W)\) 处,而 \(H=R-h,W=C-w\)。单次扩展的方案数为 \((length!)\)。我们就可以写出动态转移方程式:

\[\displaystyle{\Large{dp_{i,j}=w_{now}!\times dp_{i-1,j}\ +\ h_{now}!\times dp_{i,j-1} }} \]

这个方程的意义理解是:

当前的矩形方案数等于纵向的方案数加边上方案数加横向来的方案数乘边上方案数。

最后也是轻轻松松打下第一部分的想法。

二、多重集求排列

多重集排列是什么?可以吃吗?
首先这是一个数学工具,肯定不可以吃。
多重集排列不同于多元单重排列,我们每个元素都有给定个数 \(k_i\),对于这些元素进行排列组合的计算难度就要大一些,不过有趣的计算方式是,我们可以还是将他们看成 \(\sum k_i\) 个元素,并且进行排列组合,然后把它们每个元素种类割裂开来,筛除不合法的方案,也就是除以 \(\prod k_i!\)

多重集的公式是什么?
对于当前的二元集,我们设他们的元素个数分别为 \(k_1,k_2\),则方案个数为:

\[\displaystyle{\Large{\frac{(k_1+k_2)!}{k_1!\ k_2!}}}\\ \ \\ \displaystyle{\Large{P_{cur\_direction}=(k_1+k_2)!\times (k_1!)^{-1}\times (k_2!)^{-1}}} \]

这里的 \((k!)^{-1}\) 表示阶乘的逆。后面一个式子因为这不是数学竞赛而是信息竞赛,我们不能直接甩两个阶乘到分母上,我们利用费马小定理求出阶乘的逆就好了。

为什么需要多重集排列?
扩展过程中,同方向的不同类型扩展(如“向上”和“向下”)的顺序会产生不同的放置方案,但同类扩展(如两次“向上”)的顺序不影响(因为都是向同一方向扩展,最终矩形相同,但放置顺序不同仍算不同方案)。“填充”这个事件的方案和“扩展顺序”这个事件的方案是独立事件,对于单个扩展方向,我们做一次二元多重集求排列,意在算出正逆向的扩展顺序对当前方向方案个数的影响,由于事件是独立的,所以他的影响是个乘积。

例如:

  • 行扩展需要 \(2\) 步: \(1\) 次向上和 \(1\) 次向下。
  • 可能的顺序有:先向上再向下,或先向下再向上。这两种是不同的扩展顺序,对应不同的放置方案,因此需要计算这类顺序的总数。

实现

流程大致为:

  • 初始化阶乘和逆阶乘;
  • 求初始矩形凸壳,统计其中加点个数求出其阶乘;
  • DP 求出扩展方案个数;
  • 多重集排列求出扩展顺序方案个数;
  • 加加乘乘算出答案;
  • 十年 OI 一场空,不开——见祖宗,不取模也见祖宗。

\(\mathbb{CODE}\)

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);int R, C, N;cin >> R >> C;cin >> N;// 计算初始矩形的边界int xmin = R, xmax = 1;  // 行方向边界int ymin = C, ymax = 1;  // 列方向边界for (int i = 0; i < N; ++i) {int x, y;cin >> x >> y;xmin = min(xmin, x);xmax = max(xmax, x);ymin = min(ymin, y);ymax = max(ymax, y);}// 初始矩形的高度和宽度// 初始矩形内的空格数(需要填充的数量)// 行方向总扩展步数 H = 向上扩展数 + 向下扩展数// 列方向总扩展步数 W = 向左扩展数 + 向右扩展数int h0 = xmax - xmin + 1;int w0 = ymax - ymin + 1;int res = h0 * w0 - N;int H = R - h0;int W = C - w0;// 计算行方向的上下扩展数(up = xmin-1,down = H - up)// 计算列方向的左右扩展数(left = ymin-1,right = W - left)int up = xmin - 1;int down = H - up;int left = ymin - 1;int right = W - left;// 预处理阶乘和逆阶乘int maxn=max({res,H,W,R,C});vector<long long> fact(maxn + 1);vector<long long> inv_fact(maxn + 1);// 计算阶乘fact[0] = 1;for (int i = 1; i <= maxn; ++i) {fact[i] = fact[i - 1] * i % MOD;}// 计算逆阶乘inv_fact[maxn] = 1;long long base = fact[maxn];int exp = MOD - 2;while (exp) {if (exp & 1) inv_fact[maxn] = inv_fact[maxn] * base % MOD;base = base * base % MOD;exp >>= 1;}for (int i = maxn - 1; i >= 0; --i) {inv_fact[i] = inv_fact[i + 1] * (i + 1) % MOD;}//dp[a][b] 表示完成a次行扩展和b次列扩展的总排列数vector<vector<long long>> dp(H + 1, vector<long long>(W + 1, 0));dp[0][0] = fact[res];  // 初始状态for (int a = 0; a <= H; ++a) {for (int b = 0; b <= W; ++b) {if (a == 0 && b == 0) continue;long long val = 0;// 若可从行扩展转移(上一步是第a-1次行扩展)if (a > 0) {int current_width = w0 + b;  // 当前宽度 = 初始宽度 + 列扩展次数val = (val + dp[a - 1][b] * fact[current_width]) % MOD;}// 若可从列扩展转移(上一步是第b-1次列扩展)if (b > 0) {int current_height = h0 + a;  // 当前高度 = 初始高度 + 行扩展次数val = (val + dp[a][b - 1] * fact[current_height]) % MOD;}dp[a][b] = val;}}// 计算行扩展的多重集排列数:H! / (up! * down!)// 计算列扩展的多重集排列数:W! / (left! * right!)long long term1 = fact[H] * inv_fact[up] % MOD;term1 = term1 * inv_fact[down] % MOD;long long term2 = fact[W] * inv_fact[left] % MOD;term2 = term2 * inv_fact[right] % MOD;// 总方案数 = dp[H][W] * 行排列数 * 列排列数long long ans = dp[H][W] * term1 % MOD;ans = ans * term2 % MOD;cout << ans << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/969338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Solon AI 开发学习 - 1导引

Solon AI & MCP 是 Java 智能体开发框架,支持 Java 8-25,简化了与主流 AI 模型(如 OpenAI、DeepSeek)的集成。它提供统一接口抽象,支持多种 AI 模型(ChatModel、GenerateModel等)协作,并兼容 MCP 协议实现…

linux c 线程池

在 Linux 系统中,线程池(Thread Pool)通常用于管理并发执行的任务,以提高系统性能和资源利用率。Linux 系统本身没有内置的线程池机制,但可以通过使用系统工具、库或自定义脚本实现类似的功能。 下面我将分几个方…

linux c 文件是否存在

在 Linux 中,判断一个文件是否存在,可以使用多种命令,以下是几种常用的方法:方法 1:使用 test 或 [[ ]] 检查文件是否存在 if [ -f /path/to/file ]; thenecho + #引号 + 文件存在 + #引号 + elseecho + …

linux c 文件是否存在

在 Linux 中,判断一个文件是否存在,可以使用多种命令,以下是几种常用的方法:方法 1:使用 test 或 [[ ]] 检查文件是否存在 if [ -f /path/to/file ]; thenecho + #引号 + 文件存在 + #引号 + elseecho + …

2025 年 11 月滚珠丝杆厂家推荐排行榜,高负载滚珠丝杆,耐磨滚珠丝杆,检测仪器高速滚珠丝杆,螺母滚珠丝杆,医用自动化滚珠丝杆公司推荐

2025年11月滚珠丝杆厂家推荐排行榜:高负载与医用自动化应用优选指南 行业背景与发展趋势 滚珠丝杆作为精密传动领域的核心部件,其技术发展与制造业升级息息相关。随着工业4.0和智能制造的深入推进,高负载滚珠丝杆、…

Pjudge #21741. 【NOIP Round #5】青鱼和区间 题解

Description 鱼王青鱼的 AI 不愿意进行 996 的工作,于是要求青鱼先和它玩一个游戏。 AI 生成了 \(n\) 道题,编号为 \(1, 2, \dots, n\)。现在 AI 心里选择了其中一道题,但青鱼不知道这是哪一题。 青鱼可以向 AI 询问…

11月18日

今日进度 (1)Commit 记录 •陈鉴祥:完成 agent-svc 忙闲状态查询,全量测试通过 •何绍斌:优化支付表索引,完成数据一致性测试 •张廷智:编写数据格式化工具,完成报表页联调 •郑权:收集测试用例,组织团队交叉…

UE4/UE5反射系统动态注册机制解析 - 实践

UE4/UE5反射系统动态注册机制解析 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

完全平方和的推广

平方和公式 \[(\sum_{i=1}^n A_i)^2 = \sum_{i=1}^n A_i^2 + 2\sum_{1 \leq i < j \leq n} A_i A_j \]核心是完全平方公式的推广,用“多项式展开逻辑”即可理解。 1. 从简单到复杂推导 (1)2个数的情况(完全平方…

三维偏序整体二分?

基于整体二分的CDQ分治实现? 众所周知的是三维偏序可以使用整体二分解决,但是今天我研究了一下,发现这个整体二分并不是一般意义下的整体二分,而是CDQ分治的一种类似整体二分的实现。 一般而言,整体二分指的是平行…

做题随笔:P3403

Solution 这里给出了一种不使用最短路的优化算法,可以优化到 \(O(x)\),而且出奇的直观、好写。同见于 oi-wiki。(其实相关部分是我写的) 题意 原题链接 给定 \(x,y,z,h\),求 \(ax+by+cz=h\) 的非负整数解 \((a,b,…

2025.11.18

今天上课,拿快递,吃饭,睡觉,买水果,回宿舍。

《从纪律委员到AI元人文开放者》

《从纪律委员到AI元人文开放者》 作者:岐金兰 日期:2025年11月18日 三十年前,我站在教室的讲台上,手里拿着纪律记录本。那时的我,是班级秩序的守护者,用稚嫩的肩膀承担着维持纪律的责任。谁能想到,三十年后,我…

MEMS与CMOS的3D集成技术研究进展 - 指南

MEMS与CMOS的3D集成技术研究进展 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

CSS学习笔记(六):CSS预处理器 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

「Solution」AGC008F Black Radius

大战 6h 看题解天塌了。很好玩的题目!没有想到题解区做法,这里给一个鏖战许久的容斥做法。由于写的太多所以单独成篇了。 这个关键点、距离等限制有一种 [十二省联考 2019] 希望 的既视感(虽然这个题更早)。直接算…

linux c web

您提到的“Linux C web”可能有多种含义,以下是几种可能的解释和相关建议:1. Linux 内核开发(C 语言) 如果您是在 Linux 内核开发中使用 C 语言,以下是一些常用命令和工具: 常用命令:gcc:编译 C 代码gcc -o my…

2025 年 钢丝网/钢骨架 塑料复合管厂家权威推荐榜/哪家好/有实力/可靠的/排名企业-江苏狼博管道制造有限公司

2025 年 钢丝网/钢骨架 塑料复合管厂家权威推荐榜/哪家好/有实力/可靠的/排名企业-江苏狼博管道制造有限公司** 2025 年 钢丝网/钢骨架 塑料复合管厂家权威推荐榜**钢骨架塑料复合管 钢丝网骨架塑料复合管 钢骨架聚乙…

CSS实现修改CheckBox样式

CSS实现修改CheckBox样式checkbox的代码:<div><input type="checkbox" id="custom-checkbox" class="custom-checkbox"><label for="custom-checkbox">&l…

人工智能之编程进阶 Python高级:第二章 面向对象

人工智能之编程进阶 Python高级:第二章 面向对象人工智能之编程进阶 Python高级 第二章 面向对象@目录人工智能之编程进阶 Python高级前言一、面向对象核心概念二、定义类和创建对象1. 基本语法2. __init__ 方法三、封…