hdu1299 Diophantus of Alexandria(数学题)

读完题目可以知道

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值