TopCoder SRM 596 Div2 第3题

Problem Statement

 

For an integer n, let F(n) = (n - 0^2) * (n - 1^2) * (n - 2^2) * (n - 3^2) * ... * (n - k^2), where k is the largest integer such that n - k^2 > 0. You are given three long longslo, hi and divisor. It is guaranteed thatdivisor will be a prime number. Compute and return the number of integers n betweenlo and hi, inclusive, such that F(n) is divisible bydivisor.

Definition

 

Class:

SparseFactorialDiv2

Method:

getCount

Parameters:

long long, long long, long long

Returns:

long long

Method signature:

long long getCount(long long lo, long long hi, long long divisor)

(be sure your method is public)

 

 

Constraints

-

lo will be between 1 and 1,000,000,000,000, inclusive.

-

hi will be between lo and 1,000,000,000,000, inclusive.

-

divisor will be between 2 and 997, inclusive.

-

divisor will be a prime number.

Examples

0)

 

 

4

8

3

Returns: 3

The value of F(n) for each n = 4, 5, ..., 8 is as follows.

  • F(4) = 4*3 = 12
  • F(5) = 5*4*1 = 20
  • F(6) = 6*5*2 = 60
  • F(7) = 7*6*3 = 126
  • F(8) = 8*7*4 = 224

Thus, F(4), F(6), F(7) are divisible by 3 but F(5) and F(8) are not.

1)

 

 

9

11

7

Returns: 1

  • F(9) = 9*8*5 = 360
  • F(10) = 10*9*6*1 = 540
  • F(11) = 11*10*7*2 = 1540

Only F(11) is divisible by 7.

2)

 

 

1

1000000000000

2

Returns: 999999999999

Watch out for the overflow.

3)

 

 

16

26

11

Returns: 4

 

4)

 

 

10000

20000

997

Returns: 1211

 

5)

 

 

123456789

987654321

71

Returns: 438184668

 

类型:数论  难度:2.5

题意:给出lo,hi,div三个数,定义函数

F(n) = (n - 0^2) * (n - 1^2) * (n - 2^2) * (n - 3^2) * ... * (n - k^2)

k是满足n-k^2>0的最大整数。

求在[lo,hi]区间内满足F(n)能被div整除的数的个数。

 

分析:直接遍历[lo,hi]的所有数并判定F(n)能否被div整除不可行,应为hi-lo的数量级在10^12。

这里想到计算素数的思想(从小到大找素数,将素数的整数倍标记为非素数),考虑若F(n)能被div整除,那么F(n)的因式 (n-k^2),0<=k<√n 必定至少有一个能被div整除,也就是说 n-y^2 = div*x,所以考虑 div*x+y^2,x>=1,y>=0 这个式子的值,如果这个式子的值落在[lo,hi]区间,那么这个数就是满足要求的。那么,遍历x,y,将落在区间内的值加入一个结果set集保证不重,最后统计set的大小就是结果。但是复杂度仍高,考虑当div=2时,复杂度为10^12 * 10^6,显然不能接受。一定把复杂度降低到10^6。

 

考虑简化上一种方法,由于外层循环是遍历x,寻找落在区间内的 div*x +y^2,那么若能知道每个 [div*x,div*(x+1)) 区间内的合法的n的个数,那么再乘以x落在区间内的可能值,就能在O(1)算出结果。运用这个思路,再进一步细化,并注意边界条件。

设rc[i]表示 y^2 % div = i 时,y^2 / div 的值再加1,表示这个余数最开始在div的第几个倍数中出现。遍历所有的y,1<=y<√hi,得到rc数组的值。

再计算rc数组中出现的余数的数目cnt,即每div个数有多少个满足条件。再计算所有rc数组的最大值st,即从div的第几个倍数开始,后续的每div个数都一定有cnt个满足要求的数。

之后就是考虑lo,hi两个数的边界条件,记录[lo,div*left)区间和[div*right,hi]区间内满足要求的数的个数,left取满足div*left >=lo的最小值,right取满足div*right<=hi的最大值

再然后,考虑此时的left和st的关系,若left<st,则此时每div个数符合要求的数还不到cnt个,需遍历rc数组,递增left,直到left=st。

之后就可以计算[left,right]区间内的符合要求的个数,即cnt*(right-left)。

 

代码如下:

#include<string>
#include<cstdio>
#include<vector>
#include<cstring>
#include<map>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;

class SparseFactorialDiv2
{ 	 
	public:
		long long getCount(long long lo, long long hi, long long divisor)
		{
			long long rc[1000];
			memset(rc,-1,sizeof(rc));
			
			rc[0] = 1;
			for(long long i=1; i*i<hi; i++)
			{
				long long a = (i*i)/divisor;
				long long b = (i*i)%divisor;
				
				if(rc[b]<0) rc[b] = a+1;
			}
			
			long long st = -1, cnt = 0;
			for(long long i=0; i<divisor; i++)
			{
				if(rc[i]>-1) cnt++;
				if(rc[i]>st) st = rc[i];
			}
			
			long long lefta = lo/divisor;
			long long leftb = lo%divisor;
			long long righta = hi/divisor;
			long long rightb = hi%divisor;
			
			long long ans = 0;
			
			if(lefta==righta)
			{
				for(long long i=leftb; i<=rightb; i++) 
					if(rc[i]>0 && lefta >= rc[i]) ans++;
			}
			else
			{
				for(long long i=leftb; i<divisor; i++) 
					if(rc[i]>0 && lefta >= rc[i]) ans++;
				
				lefta++;
				for(; lefta < st && lefta < righta; lefta++)
				{
					for(long long i=0; i<divisor; i++)
						if(rc[i]>0 && lefta >= rc[i]) ans++;
				}
				for(long long i=0; i<=rightb; i++) 
					if(rc[i]>0 && righta >= rc[i]) ans++;
				
				ans += cnt*(righta-lefta);
			}
			return ans;
		}
};

int main()
{

}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值