CF599D Spongebob 和矩形
1 CF599D Spongebob 和矩形
2 题目描述
时间限制 \(2s\) | 空间限制 \(256M\)
\(Spongebob\) 已经厌倦了解释他奇怪的行为和计算,所以他简单的让你找出所有成对的 \(n\) 和 \(m\),使得在 \(n\) 行 \(m\) 列的表格中存在 \(x\) 个不同的矩形。例如,在一张 \(3×5\) 的表格中,有 \(15\) 个边长为 \(1\) 的矩形,\(8\) 个边长为 \(2\) 的矩形,\(3\) 个边长为 \(3\) 的矩形。一个 \(3×5\) 表中不同矩形的总数是 \(15+8+3=26\) 个。
数据范围:\(1 ≤ x ≤ 10^{18}\)
3 题解
我们推找规律:对于 \(1 * m\) 的矩形,我们可以放下 \(m\) 个 \(1*1\) 的正方形;对于 \(2 * m\) 的矩形,我们可以放下 \(2 * m\) 个 \(1 * 1\) 的正方形,\(1 * (m - 1)\) 个 \(2 * 2\) 的正方形;对于 \(3 * m\) 的矩形,我们可以放下 \(3 * m\) 个 \(1 * 1\) 的正方形,\(2 * (m - 1)\) 个 \(2 * 2\) 的正方形,\(1 * (m-2)\) 个 \(3 * 3\) 的正方形。
我们发现:对于 \(n * m\) 的矩形(设 \(n \le m\)),我们可以放下的最大的正方形为 \(n * n\),该正方形最多可以放 \(1 * (m - n+1)\) 个。这是因为在最右的一个正方形放置时,我们需要 \(n - 1\) 列格子,再往右放置会超出矩形。因此,我们最右的正方形必须放在第 \(m - n + 1\) 列,而小于等于这一列的每一列都可以放置一个正方形,所以总共的个数为 \(1 * (m - n + 1)\) 个。而对于 \((n-1) * (n-1)\) 的正方形,最右边的正方形放置时需要 \(n-2\) 列格子,同时最下面的正方形需要 \(1\) 行格子,所以在列上最多能放 \((m - n + 2)\) 个 \((n-1) * (n-1)\) 的正方形,在行上最多能放 \(2\) 个 \((n-1) * (n-1)\) 的正方形,总共就可以放 \(2 * (m - n +2)\) 个这样的正方形。
相信大家已经看出了规律:对于 \((n - i) * (n - i)\) 的正方形,我们在列上最多可以放 \((m - n + i + 1)\) 个,在行上最多可以放 \((i+1)\) 个,总共可以放 \((i+1) * (m - n + i + 1)\) 个。那么答案就是 \(\sum_{i = 0}^{n - 1} \limits (i + 1) * (m - n + i + 1)\)。将所有的 \(i\) 都 \(+ 1\),就可以得到 \(\sum_{i = 1}^{n} \limits i * (m - n + i)\) 这个式子。
我们发现:当 \(n\) 固定时,答案最小为 \(\sum_{i = 1}^n \limits i * i\)。而当 \(n = 2* 10^6\) 时,\(\sum_{i = 1}^n \limits i * i\) 就已经比 \(10^{18}\) 要大了。所以我们可以枚举 \(n\) 的大小,然后找到其对应的 \(m\)。我们如何找到这个 \(m\) 呢?观察发现:\(m\) 每增加 \(1\),整个式子的值就增加 \(\frac{n(n+1)}{2}\)。我们就可以判断 \(x - \sum_{i = 1}^n \limits i * i\) 是否为\(\frac{n(n+1)}{2}\) 的倍数。如果是,那么 \(m\) 的值便为 \(n + \frac{2(x - \sum_{i = 1}^n \limits i * i)}{n(n+1)}\)。否则直接 \(continue\)。
4 代码(空格警告):
#include <iostream>
using namespace std;
const int N = 2e6+10;
#define int long long
int x, in, cnt, ans;
int sum[N];
signed main()
{
for (int i = 1; i <= 2e6; i++) sum[i] = sum[i-1] + i * i;
cin >> x;
for (int i = 1; i <= 2e6; i++)
{
in = sum[i];
if (in > x) break;
if (in == x) ans++;
else
{
if ((x - in) % ((i * (i+1)) / 2) != 0) continue;
cnt = (x - in) / ((i * (i+1)) / 2);
ans += 2;
}
}
cout << ans << '\n';
for (int i = 1; i <= 2e6; i++)
{
in = sum[i];
if (in > x) break;
if (in == x) cout << i << " " << i << '\n';
else
{
if ((x - in) % ((i * (i+1)) / 2) != 0) continue;
cnt = (x - in) / ((i * (i+1)) / 2);
cout << i << " " << i + cnt << '\n';
}
}
for (int i = 2e6; i >= 1; i--)
{
in = sum[i];
if (in > x) continue;
else if (in != x)
{
if ((x - in) % ((i * (i+1)) / 2) != 0) continue;
cnt = (x - in) / ((i * (i+1)) / 2);
cout << i + cnt << " " << i << '\n';
}
}
return 0;
}