题解:AtCoder AT_awc0101_d Tiling Plan
【题目来源】
AtCoder:D - Tiling Plan
【题目描述】
Takahashi works at a renovation company and is responsible for covering room floors with square tiles.
Takahashi has received requests to tile the floors of \(N\) rectangular rooms. The floor of the \(i\)-th room has height \(H_i\) and width \(W_i\).
Takahashi wants to choose exactly one type of square tile and use it to cover all room floors. In each room, tiles are placed parallel to the room's edges without gaps or overlaps. The side length of the square tile must be a positive integer.
The necessary and sufficient condition for a square tile with side length \(d\) to be able to tile the floor of room \(i\) is that both \(H_i\) and \(W_i\) are multiples of \(d\). In this case, the number of tiles used in room \(i\) is \(C_i = \frac{H_i}{d} \times \frac{W_i}{d}\).
Furthermore, due to design requirements, each room has a specified "design coefficient" \(S_i\). The number of tiles \(C_i\) used in room \(i\) must be a multiple of \(S_i\).
Takahashi wants to use tiles that are as large as possible. Find the maximum side length \(d\) of a square tile that simultaneously satisfies the above conditions for all rooms.
高橋在一家装修公司工作,负责用正方形瓷砖铺设房间地板。
高橋收到了为 \(N\) 个矩形房间铺设地板的请求。第 \(i\) 个房间的地板高度为 \(H_i\),宽度为 \(W_i\)。
高橋想选择恰好一种正方形瓷砖,并用它来铺设所有房间的地板。在每个房间中,瓷砖平行于房间的边放置,不能有间隙或重叠。正方形瓷砖的边长必须是正整数。
边长为 \(d\) 的正方形瓷砖能够铺设房间 \(i\) 的地板的充分必要条件是 \(H_i\) 和 \(W_i\) 都是 \(d\) 的倍数。此时,房间 \(i\) 使用的瓷砖数量为 \(C_i = \frac{H_i}{d} \times \frac{W_i}{d}\)。
此外,由于设计要求,每个房间有一个指定的"设计系数" \(S_i\)。房间 \(i\) 使用的瓷砖数量 \(C_i\) 必须是 \(S_i\) 的倍数。
高橋希望使用尽可能大的瓷砖。求能同时满足上述所有房间条件的正方形瓷砖的最大边长 \(d\)。
【输入】
\(N\)
\(H_1\) \(W_1\) \(S_1\)
\(H_2\) \(W_2\) \(S_2\)
\(\vdots\)
\(H_N\) \(W_N\) \(S_N\)
- The first line contains an integer \(N\) representing the number of rooms.
- From the 2nd line to the \((N + 1)\)-th line, information about each room is given.
- The \((1 + i)\)-th line contains the height \(H_i\), width \(W_i\), and design coefficient \(S_i\) of the \(i\)-th room, separated by spaces.
【输出】
Output in one line the maximum value of the side length \(d\) of a square tile that satisfies the conditions.
【输入样例】
2
6 8 6
10 12 15
【输出样例】
2
【核心思想】
-
问题分析:给定 \(N\) 个矩形房间,每个房间高度 \(H_i\)、宽度 \(W_i\)、设计系数 \(S_i\)。需要找到一个最大的正整数边长 \(d\),使得对所有房间同时满足:(1) \(d \mid H_i\) 且 \(d \mid W_i\)(瓷砖能完整铺设);(2) \(C_i = \frac{H_i}{d} \times \frac{W_i}{d}\) 是 \(S_i\) 的倍数(设计约束)。关键在于将"同时满足所有房间"的条件转化为对 \(d\) 的约束范围。
-
算法选择:
- 欧几里得算法(GCD):利用 \(\gcd\) 的性质将条件(1)转化为 \(d\) 必须是所有 \(\gcd(H_i, W_i)\) 的公共因子的约束
- 因子枚举 + 验证:先求出所有可能 \(d\) 的候选集(即总 \(\gcd\) 的所有因子),然后从大到小验证条件(2)
-
关键步骤:
- 计算总 \(\gcd\):
- 对每个房间计算 \(g_i = \gcd(H_i, W_i)\)
- 累计算 \(g = \gcd(g_1, g_2, \ldots, g_N)\),则 \(d\) 必须是 \(g\) 的因子
- 枚举候选 \(d\):
- 枚举 \(g\) 的所有因子,存入候选数组(通过 \(i\) 从 \(1\) 到 \(\sqrt{g}\) 枚举,避免遗漏)
- 从大到小验证:
- 将候选因子按降序排列,对每个 \(d\) 检查是否对所有房间满足 \((\frac{H_i}{d} \times \frac{W_i}{d}) \bmod S_i = 0\)
- 第一个满足条件的 \(d\) 即为最大边长
- 输出该 \(d\)
- 计算总 \(\gcd\):
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log C + \sqrt{g} \cdot N)\),其中 \(C = \max(H_i, W_i)\)。计算总 \(\gcd\) 为 \(O(N \log C)\),枚举因子 \(O(\sqrt{g})\),每次验证 \(O(N)\)
- 空间复杂度:\(O(N + \sqrt{g})\),存储房间信息数组和因子候选数组
-
欧几里得算法的核心思想:
- 约束降维:将" \(d\) 同时整除所有 \(H_i\) 和 \(W_i\) "的 \(2N\) 个约束,通过 \(\gcd\) 合并为" \(d\) 是 \(g\) 的因子"这单一约束,大幅缩小搜索空间
- 因子枚举策略:一个数的因子成对出现(\(i\) 和 \(\frac{g}{i}\)),只需枚举到 \(\sqrt{g}\) 即可获取全部因子,时间复杂度从 \(O(g)\) 降至 \(O(\sqrt{g})\)
- 贪心验证顺序:将候选因子降序排列后依次验证,第一个满足条件的即为最大值,避免额外比较
- 整除性传递:\(\gcd\) 的嵌套性质 \(\gcd(\gcd(a,b), c) = \gcd(a,b,c)\) 保证了多组约束的优雅合并
- 适用于多组整除约束下的最优因子搜索问题,将指数级搜索空间通过数论性质压缩至根号级别
【算法标签】
欧几里得算法
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将 int 定义为 long long,防止乘法溢出
const int N = 100005; // 最大房间数量
int n; // 房间数量
int h[N], w[N], s[N]; // h[i]: 房间i高度, w[i]: 房间i宽度, s[i]: 房间i设计系数
vector<int> divisors; // 存储所有可能的候选边长d
// 检查边长d是否满足所有房间的条件
// 条件:对每个房间i,(H_i/d) * (W_i/d) 是 S_i 的倍数
bool check(int d)
{
for (int i = 1; i <= n; i++)
{
// 计算房间i使用的瓷砖数量,并检查是否为S_i的倍数
// 注意:由于d是gcd的因子,H_i和W_i一定是d的倍数,所以除法是整除
if ((h[i] / d * w[i] / d) % s[i] != 0)
return false; // 不满足条件
}
return true; // 所有房间都满足
}
signed main() // 使用 signed 替代 int,因为 #define int long long
{
cin >> n; // 读入房间数量
int g = 0; // g: 所有房间(H_i, W_i)最大公约数的GCD
// 读入每个房间的信息
for (int i = 1; i <= n; i++)
{
cin >> h[i] >> w[i] >> s[i];
// 计算房间i的高度和宽度的GCD
int gi = __gcd(h[i], w[i]);
// 所有房间GCD的GCD,即d必须能整除所有H_i和W_i
// 所以d必须是g的因子
g = __gcd(g, gi);
}
// 枚举g的所有因子,作为候选边长d
for (int i = 1; i * i <= g; i++)
{
if (g % i == 0) // i是g的因子
{
divisors.push_back(i); // 小因子
if (i * i != g)
divisors.push_back(g / i); // 大因子(避免重复)
}
}
// 按升序排序,方便从大到小枚举
sort(divisors.begin(), divisors.end());
// 从大到小枚举候选边长,找到第一个满足条件的即为最大边长
for (int i = divisors.size() - 1; i >= 0; i--)
{
if (check(divisors[i]))
{
cout << divisors[i] << endl; // 输出最大满足条件的边长
return 0;
}
}
return 0;
}
【运行结果】
2
6 8 6
10 12 15
2
浙公网安备 33010602011771号