[LightOJ - 1245][数论]Harmonic Number (II)

链接:https://vjudge.net/contest/177082#problem/G
题目:
大意是讲通过公式H(n)计算出序列总和。

解题思路:
一开始我在网上搜了很多关于这道题的题解,但始终不是很懂为什么可以这样做,以及为啥要加上特判,到后面自己打表以后才慢慢懂一些。
为了更直观一些,打表结果如下:(左边数字是n/i可能数,右边是有多少个)
这里写图片描述

根据打表结果发现:
1>发现有一些元素有很多个,比如"1",那么怎么快速计算出这些元素有多少个呢?

可以根据公式:这里写图片描述 算出结果。(就是算出i到i+1之间有多少个数)
比如当n=10时,有:这里写图片描述

2>每行元素的数量与sqrt(n)大小有关,但要根据条件判断是否减1,在仔细分析表后,发现似乎与n-sqrt(n)*sqrt(n)大小有关,如下:
(灵魂画图,凑合着看)

这里写图片描述

如果n-sqrt(n)*sqrt(n)<sqrt(n)的话,那么说明元素种类是奇数,因为在计算和的时候是前后一起加的,所以最后要减去多加上的n/sqrt(n)。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
#define MM(a) memset(a,0,sizeof(a))
#define shutdown cin.sync_with_stdio(false);cin.tie(0);cout.sync_with_stdio(false);cout.tie(0)
//int b[1001][1001];//打表专用
//void init() {
//    for(int i=1; i<=100; i++) {
//        for(int j=1; j<=i; j++) {
//            b[i][j]=i/j;
//        }
//    }
//    for(int i=1; i<=100; i++) {
//            int temp=b[i][1],flag=1;
//        for(int j=1; j<=i; j++) {
//            if(b[i][j+1]==temp) flag++;
//            else{
//                cout<<temp<<":"<<flag<<" ";
//                temp=b[i][j+1];
//                flag=1;
//            }
//        }
//        cout<<endl;
//    }
//}
ll n;
ll H(ll n) {
    ll res = 0;
    int i,sq=sqrt(n);
    for(i=1;i<=sq;i++){
        res+=(n/i-n/(i+1))*i+n/i;//开头和尾部一起算
    }
    if(n-sq*sq<sq)//如果元素的种类是奇数
        res-=n/(i-1);//减去多加的
    return res;
}
int main() {
    shutdown;
//    MM(b);
//    init();
    int t;
    cin>>t;
    for(int i=1; i<=t; i++) {
        cin>>n;
        cout<<"Case "<<i<<": "<<H(n)<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值