首页 > 代码库 > POJ 2800 Joseph’s Problem 数论找规律
POJ 2800 Joseph’s Problem 数论找规律
Description
求
Input
两个整数n和k(1<=n,k<=1e9)
Output
输出
Sample Input
5 3
Sample Output
7
暴力超时,这样就打下表找下余数的规律。输入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\n",sum); } return 0;}
POJ 2800 Joseph’s Problem 数论找规律