POJ 2800 Joseph’s Problem 数论找规律

Description 
这里写图片描述 
Input 
两个整数n和k(1<=n,k<=1e9) 
Output 
输出这里写图片描述 
Sample Input 
5 3 
Sample Output 

暴力超时,这样就打下表找下余数的规律。输入100,27,一下子就可以看出来,倒着的看,是一段一段的等差序列。

例如100 25

除数 1    2    3    4    5    6    7    8    9     10     11    12   13 14  15  ......25   26........

 商  25  12   8    6    5    4    3    3    2     2       2      2      1   1   1   ........1    0.....

这里可以看到商的变化前面很大,从5后面开始稳定下来,观察,其实就是sqrt(k),前面除数数小,商的变化就大,这个临界点也与求质数打表那个相似,从2~sqrt(质数),遍历判断,如果没有能整除的即为质数,这两个的数学原理好像是相同的,想想为什么?

sqrt(k) = e;

然后我们利用除数来求等差数列的区间。也就是枚举商sqrt(k)~1,左区间p1 =  k/e+1,得的是左区间,然后k/(e-1)得右区间,注意e这个除数其实不是起点,得到相同商的第一个起点是e+1,然后利用e-1得到这个相同的商的最大的一个除数,比如25/3=8,这个等差区间的最后一个,25/2=12,这个等差区间的最后一个。

除数i大于k的部分,数量是n-k,然后直接k*(n-k)

小于sqrt(k)的部分直接枚举就行了

****然后之前还有一个问题,按照HJ的写发,p1是k/e,是相同商的前一个区间的最后一个数,所以求和公式,数量不用+1,但是上底+1,再加下底,他的这个写发有一个问题是:

上面例子 n=8的时候,3 3这两个商的区间在i=4的时候算完了,然后i=3,p1=8,p2=12大于n了,p2=8,然后求和相减等于0,这部分其实多算了一遍,区间就已经没有了。跳出的上一个循环肯定是完成了p2=n的操作。比如继续p1=25/2=12明显大于n了,跳出。按照我这种写法,n=8,算完除数为3的,p1=9直接跳出了。********

总思想其实就是:这个等差序列的第一个是前一个等差序列最大除数的下一个,最后一个就是这个等差序列的最大除数,在代码的循环里面也是循环到2就结束,因为2算的就是商为1的情况。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int main()
{
    freopen("joseph.in","r",stdin);
    freopen("joseph.out","w",stdout);
    ll n,k;
    while(~scanf("%I64d%I64d",&n,&k))
    {

        ll sum=0;
        //第三部分
        if(n>k)
            sum+=(n-k)*k;
        ll e=sqrt(1.0*k);
        ll p=k/e;
        //第一部分
        for(int i=1;i<=n && i<=p;i++)
            sum+=k%i;
        //第二部分
        for(int i=e;i>1;i--)
        {
            ll p1=k/i + 1;
            ll p2=k/(i-1);

            //n小于左区间了就直接退出了
            if(p1>n)
                break;

            //当n的大小小于这段等差数列区间的最后一个值
            if(p2>n)
                p2=n;

            sum+=(k%(p1)+k%p2)*(p2-p1+1)/2;//等腰求和
        }
        printf("%I64d
",sum);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zhangmingzhao/p/7256582.html