勾股定理
勾股定理
题目
题目描述
小 \(A\) 最近在学习勾股定理的时候发现了一个很有趣的教学用具,于是买了一个回家:
小 \(A\) 买来的用具的短直角边长度为 \(i cm\),长直角边长 \(j cm\),厚度为 $ 1cm$。到货后,小 \(A\) 发现他需要自己给容器装水(一共要装 \(i^2+j^2cm^3\) 液体),于是又需要买一些蓝色的液体。
他发现商店里卖的蓝色液体只有 \(m cm^3\) 一瓶的,本着不浪费的原则,他想知道是否存在一个数 \(k\),使小 \(A\) 买 \(k\) 瓶的容量为 \(m cm^3\)
瓶的蓝色液体可以正好装满用具。
这个问题对于小 \(A\) 来说太简单了,所以他想知道有多少个教学用具满足下面条件:
- 其短直角边长度和长直角边长度都是整数。
- 短直角边长度 \(\le\) 长直角边长度,且长直角边长 \(\le n cm\)。
- 存在一个数 \(k\),使小 \(A\) 买 \(k\) 瓶的容量为 \(m cm^3/\text{瓶}\) 的蓝色液体可以正好装满用具。
输入格式
一共一行两个整数,分别表示 \(n,m\)。
输出格式
一行一个整数,表示答案。
样例数据
【样例 \(1\) 输入】
6 3
【样例 \(1\) 输出】
3
【样例 \(1\) 解释】
合法的教学用具为 \((3,3),(3,6),(6,6)\),其中 \((x,y)\) 表示短直角边为 \(x\) 长直角边为 \(y\) 的教学用具。
【样例 \(2\) 输入】
949 216
【样例 \(2\) 输出】
702
数据范围
对于所有数据 \(n\le10^9,m\le10^6\)。
测试点 | 数据范围 |
---|---|
\(1-4\) | \(n,m\le30\) |
\(5-8\) | \(n\le1000\) |
\(9-10\) | \(m=1\) |
\(11-12\) | \(m=2\) |
\(13-15\) | \(n\le10^6,m\le1000\) |
\(16-18\) | \(m\le1000\) |
\(19-20\) | 无限制 |
题解
- 考点:数论,简单计数
- 难度:CSP-J T3
我们可以依次考虑每一个 \(i^2\mod m\) 是多少,然后记 \(f_i\) 为 \(i^2\mod m=j\) 的这样的 \(i\) 的个数。
那么如果不考虑 \(i\mod m=j\),答案就是 \(\sum_{i=0}^{m-1}f_if_{m-1}\mod m\)。
如何处理 \(i\le j\),那么我们可以把 \(i=j\) 的情况再算一次之后,把整个答案除以 \(2\)。
如何计算 \(2i^2\mod m=0\) 的个数?实际上只有当 \(i^2\mod m=0\) 或 \(\frac{m}{2}\) 才可以,那么方案数就是 \(f_0+f{\frac{n}{2}}\)。
随后可以发现难点在于得到 \(f_i\),其实对于所有 \(i\mod m\) 相同的 \(i\),其 \(i^2\mod m\) 的值也相同。
而满足 \(i\mod m=j\) 的 \(i\) 的个数是好处理的。
所以就可以 \(O(m)\) 的时间复杂度内解决此题。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int kMaxN = 1e6 + 10;
int f[kMaxN], n, m, ans;
signed main() {
freopen("pythagorean.in", "r", stdin);
freopen("pythagorean.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 0; i < m; i++) {
f[i * i % m] += n / m, f[i * i % m] += ((n % m >= i) && (i ? 1 : 0));
}
for (int i = 0; i < m; i++) {
int j = (m - i) % m;
if (i == j) {
ans += (f[i] + 1) * f[i];
} else {
ans += f[i] * f[j];
}
}
cout << ans / 2 << '\n';
return 0;
}