首先一看题目就是个数学题,而且数据范围比较大,需要
O(n)
或
O(nlog2n)
级别的算法才能通过。直接从枚举相同的幂的角度来入手,显然是不可行的。
我们需要换个角度,同样是枚举,怎样枚举才能快速考虑所有可能的情况呢?这就需要我们通过观察或分析,得到
ab=cd
的一些性质。
如果对一些小范围的数据,将所有情况输出,可以提出这样一个猜想:任意的
ab=cd
都可以化成
(xy)b=(xz)d
的形式。
下面就来证明这个猜想。
考虑我们对
ab
进行质因数分解,得到形如
(d1)k1×(d2)k2×…×(dm)km
的结果,则显而易见
cd
质因数分解后得到的结果和
ab
相同。
根据幂的乘方的逆运算法则,可以将上述结果化为
[(d1)k1÷b]b×[(d2)k2÷b]b×…×[(dm)km÷b]b
的形式,由此不难看出任意的
ki
都为
b
的倍数。
把公共指数
类似地,有
于是我们设
x=∏di
,显然可以将
a
和
明确了这条基本事实之后,就可以枚举
x
、
现在问题是,对于已知的
xy
和
xz
,存在多少对
(b,d)
使得
(xy)b=(xz)d
。
可以变形为
xyb=xzd
,亦即
yb=zd=f
。显然最小的
f
就是
这时应想想题目的限制条件。在
ab=cd
中,要求
a,b,c,d
均不超过
n
。对
而对
d
也是类似的。题目要求满足
变形得到
根据不等式组“同小取小”的原则,可得
不难发现两个式子的分母其实是一样的,则只需考虑分子当中的
y
和
答案必须是整数,而且应该向下取整。这样我们就得到了,对于确定的
x
、
这样就完美解决了问题。但是要注意 x=1 是比较特殊的情况,应该一开始就单独考虑,之后令 x <script type="math/tex" id="MathJax-Element-62">x</script> 从 2 开始枚举。
参考代码:
//2004.cpp
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
#define aspe MOD
const long long MOD = 1e9 + 7;
const long long MAXN = 1e6 + 1;
inline long long gcd(long long x, long long y) { return y ? gcd(y, x % y) : x; }
bool flag[MAXN];
int main(void) {
freopen("2004.in", "r", stdin);
freopen("2004.out", "w", stdout);
for (long long i = 2; i * i <= MAXN; i++)
for (long long j = i * i; j <= MAXN; j *= i)
flag[j] = true;
long long T; cin >> T; //printf("%lld\n", T);
while (T--) {
long long N; cin >> N; //cout << N << endl; //printf("%lld\n", N);
long long ans = (long long)N * N % aspe;
for (long long k = 2; k <= N; k++) {
if (flag[k]) continue; //cout << k << endl;
for (long long x = 1, kx = k; kx <= N; x++, kx *= k)
for (long long y = 1, ky = k; ky <= N; y++, ky *= k) {
(ans += min(x, y) * N / (x * y % aspe / gcd(x, y))) %= aspe;
// printf("k = %d, x = %d, y = %d, ans = %d\n", k, x, y, ans);
}
}
cout << ans << endl;
}
return 0;
}