读完题目可以知道
x的范围是:(n,2n], y的范围是:[2n,+无穷)
我的想法就是暴力枚举x,判断对应方程的解y是不是整数
这样做理论上是有可行性的,但是好像没有办法判断y是否为整数
并且很可能超时
百度了一下,发现了很牛的答案,但是很多人的解释都是错误的!
做法是:
令y=n+k,则k一定大于等于n;(好多人都写的是小于等于)
将其代入方程可得到x=(n*n)/k+n
则若要令x为整数必须有n*n整除k,即求n*n的因子中大于等于k的因子的个数
因为因子是两两对应的,所以最后答案ans应为(因子总数+1)/2--------(因为n对应的因子仍为n)
那么怎么求一个数因子的个数呢,很简单!用欧拉函数的变形即可
例如n=p1^e1*p2^e2*...*pr^er(保证pi,pj都为素数)
则可知n的因子总个数为(1+e1)*(1+e2)*...*(1+er)
现在把n*n代入即得到n*n=p1^2e1*p2^2e2*...*pr^2er
因此n*n的因子总个数为(1+2*e1)*(1+2*e2)*...*(1+2*er)
上面对应n*n的唯一分解式,是不是很容易联想到欧拉函数呢?
代码如下:
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 10010
#define LL long long
using namespace std;
int euler_phi(int n) {
int ans = 1;
int cnt;
int m = (int)sqrt(n+0.5);
for(int i=2; i<=m; ++i) {
if(n % i == 0) {
cnt = 0;
while(n%i == 0) {
n /= i;
cnt++;
}
ans *= (1+2*cnt);
}
}
if(n > 1) {
ans *= 3;
}
return (ans+1)/2;
}
int main(void) {
int T, n, cnt;
scanf("%d", &T);
for(int t=1; t<=T; ++t) {
scanf("%d", &n);
printf("Scenario #%d:\n%d\n\n", t, euler_phi(n));
}
return 0;
}
过题后,手贱的看了一下自己代码的排名,发现排在几十名,就想着刷的靠前一点,结果一下午就耗进去了
发现别人生成素数表再依次判断素数是否为其因数,很明显这样能节省很多时间,不需要判断那些明显不是素数的数
写过之后发现排在第五,然后坑爹的一下午就开始了,我一共交了有二三十遍了吧...
我直接把会用到的素数存在数组中,但这样并没有节省多少内存
后来一直卡在我的代码耗时15ms,多次测试发现在c++程序下对普通数据用cin比较快
改为cin后,就可以了,之后一直在调节内存,但最好只能排在第三名
后来改用c语言写,就变为第一了!
截图:
代码如下:
#include <math.h>
#include <stdio.h>
int T, i, t, n, ans, cnt;
static int p[40] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,107, 109, 113, 127, 131, 137, 139, 149, 151, 163, 167,173, 179};
int main(void) {
scanf("%d", &T);
for(t=1; t<=T; ++t) {
scanf("%d", &n);
ans = 1;
for(i=0; i<40; ++i) {
if(n%p[i] == 0) {
cnt = 1;
n /= p[i];
while(n%p[i] == 0) {
cnt++;
n /= p[i];
}
cnt *= 2;
ans *= cnt+1;
}
}
if(n > 1)
ans *= 3;
printf("Scenario #%d:\n%d\n\n", t, ans/2+1);
}
return 0;
}