zoukankan      html  css  js  c++  java
  • BZOJ-1968 COMMON 约数研究 数论+奇怪的姿势

    1968: [Ahoi2005]COMMON 约数研究
    Time Limit: 1 Sec Memory Limit: 64 MB
    Submit: 1513 Solved: 1154
    [Submit][Status][Discuss]

    Description
    这里写图片描述

    Input
    只有一行一个整数 N(0 < N < 1000000)。

    Output
    只有一行输出,为整数M,即f(1)到f(N)的累加和。

    Sample Input
    3

    Sample Output
    5

    HINT

    Source
    Day2

    奇怪的姿势,不过非常神!
    

    求1-n的所有数的约数的个数,不妨可以转化一下,题目中f【i】为i的约数个数,但在做题中不妨从1~枚举i,计算后的f【i】表示1~n中约数包含i的数的个数,即约数i对答案的贡献。
    然后,思考快速的方法去实现上述要求,不妨先小规模打表找找规律(于是打出N=14的表):
    这里写图片描述
    于是我们发现了一个规律,所有对答案贡献相同的i,是必定相邻的,那么要想优化时间,不妨能够枚举中多步跳跃?
    于是开始找规律:
    首先,一个数i,它对答案的贡献(1~n中 约数包含i的数的个数)为 n/i下取整…(如何证明?)
    十分的简单,首先,假使n%i==0,那么1~n中,约数包含i的数的个数必定是n/i,那么当n%i!=0,那些多出来的数的约数是不含i的,那么剩下的便同整除时一样。得证。。。
    那么再发现一个规律, 试计算n/(n/i)(/为C++中整除),i取不同的值,于是发现,在同一个周期中(暂且称那些贡献相同的数为一个周期),上述计算值相同,且都为这个周期的末尾。
    于是高效率的做法就是,每次都增加n/(n/i)步,答案用(n/(n/i)-i+1)*(n/i)来统计即可,如此效率大概是O(logn)级的,于是顺利完成。

    简短的代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    long long ans=0;
    int n;
    
    int main()
    {
        scanf("%d",&n); 
        for (int i=1,j=0; i<=n; i=j+1)
            j=n/(n/i),ans+=(j-i+1)*(n/i);
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    349. Intersection of Two Arrays
    346. Moving Average from Data Stream
    345. Reverse Vowels of a String
    344. Reverse String
    342. Power of Four
    POJ2823 Sliding Window
    《STL源码剖析》笔记
    [jobdu]扑克牌顺子
    [jobdu]第一个只出现一次的字符
    [jobdu]包含min函数的栈
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346203.html
Copyright © 2011-2022 走看看