为您提供N
对数字n
,k
。对于每对,计算P
的除数。
P = k^n * (1 + 2 + 3 + ... + k)
从nr_div_huge.in
中读取N
,然后再读取N
对n and k
。在输出文件nr_div_huge.out
中的每一行上,写有每对P的除数的数量。因为它可能很大,所以将以1.000.000.007的模数显示。
示例:
nr_div_huge.in
2
2 3
4 4
nr_div_huge.out
8
20
说明:
对于第一对,P = 54和54具有8个除数。 对于第二对,P = 2560和2560有20个除数。
那是我的代码:
#include <iostream>
#include <fstream>
#define MOD 1000000007
using namespace std;
ifstream fin ("nr_div_huge.in");
ofstream fout("nr_div_huge.out");
bool sieve[30000];
long long int prime[3000];
int N, k, n, r, P;
//making the Sieve of Eratosthenes to have in prime array the prime numbers up to 30000
void Erat()
{
int r=0;
sieve[0]=sieve[1]=1;
for(int i=2; i*i<=30000; i++)
if(sieve[i]==0)
for(int j=2; j<=30000/i; j++)sieve[i*j]=1;
for(int i=1; i<=30000; ++i)
if(!sieve[i])prime[++r]=i;
}
//finding the numbers of divisors with Euler formula
int divisors(unsigned int n, unsigned int power)
{
int r=0, d, p, nr=1;
d=prime[++r];
while(n>1)
{
p=0;
while(n%d==0)
{
p++;
n/=d;
}
if(d==2 && p>0)
nr=(1LL*nr*(p*power))%MOD;
else if(p>0)
nr=(1LL*nr*(p*power+1))%MOD;
if(prime[r+1]==0)d++;
else d=prime[++r];
if(d*d>n)d=n;
}
return nr;
}
/*
I've transformed the formula P = k^n * (1 + 2 + 3 + ... +k) into P= k^(n+1)*(k+1)/2
Also, I've used this property:
If we have 2 numbers a and b, which are co-prime numbers, a will have n divisors and b m divisors,
the a*b number will have m*n divisors.
*/
int NrP(long long int n,long long int k)
{
unsigned long long int power=1,nrdk1,nrdkn,res=0;
nrdk1=divisors(k+1,1);
nrdkn=divisors(k,n+1);
res=(1LL*nrdk1*nrdkn)%MOD;
return res;
}
int main()
{
Erat();
fin>>N;
for(int i=1; i<=N; i++)
{
fin>>n>>k;
fout<<NrP(n,k)<<'\n';
}
return 0;
}
算法说明:
我一开始就制作了Eratosthenes筛子,以使prime
数组中的质数最大为30000。为了更快,为了计算1 + 2 + 3 + ... + k
的总和,我使用公式(k*(k+1))/2
,P变成(k^(n+1) * (k+1))/2
。由于k和k + 1是互质的,因此k ^(n + 1)和k + 1也将互质,其中之一将是偶数。为了消除/2
,我将偶数相除。因为数字是互质数,所以除数为n*m
,其中n
是k^(n+1)
的除数,m
是{{ 1}}。
为了计算除数,我使用了欧拉公式:
k+1
我需要一个更快的解决方案。
答案 0 :(得分:1)
素数分解k
。假设k
是2*2*2*3*5 = 120
。
因此,k
的任何因数必须为2^x * 3^y * 5^z
,其中x
,y
,z
可以为[0,1,2]
,[0,1]
,{{ 1}}。
同样,[0,1]
的除数必须采用k^n
的形式,其中2^x*3^y*5^z
,x
,y
可以是z
,{{1} },[0,1 .. 3*n]
。
这样,您可以与这些元素形成唯一组合的数量就是所有选择的乘积:[0, .. n]
(如果不清楚,请考虑类比计算冰淇淋圣代的独特组合数,其中冰淇淋圣代由3种可能的冰淇淋口味中的1种和4种可能的冰淇淋浇头中的1个组成...这样一来,唯一的圣代总数为3 * 4。在这里,我们的选择是使用[0, .. n]
个,(3*n+1)*(n+1)*(n+1)
个和2
个。每个独特的组合都会产生独特的产品。)
还将3
的素数添加到素数计数中,它应该可以工作。
编辑:我看到您提出的解决方案已经在使用Euler函数(5
)。
此处概述的解决方案也使用了该方法,但是缺少的见解是(k * (k+1)) / 2
的素因数分解很容易从n = p1^e1 * p2^e2 * … * pk^ek - prime factorization
的素因数分解推导:{{1}的素因数分解}是k^2
,而k
的素数分解是12
。因此,您无需计算2^2 * 3
。您只需要对12*12 = 144
进行素数分解,然后使用此数据得出2^2 * 3 * 2^2 * 3 = 2^4 * 3^2
的素数分解。
==编辑2 ==
之后,您仍然会有错误,因此某些k^n
的计算值需要是k
吗?另外,我看到您的筛子上升到3000,但不应该上升到sqrt(10 ^ 9)吗?
答案 1 :(得分:1)
您必须先做一些数学运算才能使问题更简单。如果不这样做,您将结束计算数字,该数字不能放在long long
变量中,这需要像gmp这样的多精度库并且要花费太多时间。
但是一旦您观察到1 + 2 + ... + k仅为k *(k + 1)/ 2,您将得到以下结果:
P = k^(n+1) * (k+1) / 2
而k
和k+1
是互素数(连续整数的平凡性质)。然后,您只需分解素数中的k
和k+1
。其中之一可被2整除。但这即使不计算P
其主要因子分解,也能使您。
从那里开始,您有P = a0^b0 * ... * ax^bx
,除数为(b0+1)*...*(bx+1)
。
示例应用:
您仍然必须用C ++编写算法,但是它比大量运算要有效得多。
这是可能的C ++实现
#include <vector>
#include <iostream>
#include <fstream>
// Will search all primes up to (SQSIZE * SQSIZE) - 1
constexpr const unsigned long SQSIZE = 200;
std::vector<unsigned> make_sieve(unsigned sqsize) {
unsigned size = sqsize * sqsize;
std::vector<unsigned> sieve(size, 0);
// Classic Eratosthene
for (unsigned i=2; i<sqsize; i++) {
if(sieve[i] == 0) {
for(unsigned j = i*i; j<size; j+=i) {
sieve[j] = 1;
}
}
}
unsigned len=0;
// Pack prime number in the vector
for (unsigned i=2; i<size; i++) {
if (sieve[i] == 0) sieve[len++] = i;
}
sieve.resize(len);
return sieve;
}
unsigned long n_divisors(unsigned long k, unsigned long n, const std::vector<unsigned> &sieve) {
unsigned long n_div = 1;
for(unsigned i: sieve) {
unsigned exp = 0;
while ((k % i) == 0) {
exp += 1;
k /= i;
}
// special processing if k divisible by 2: use k^n/2
if ((i == 2) && (exp != 0)) n_div *= (exp * n - 1) + 1;
else n_div *= exp * n + 1;
if (k == 1) break;
}
if (n_div == 1) n_div = 2; // k is prime and greater sieve max
return n_div;
}
int main() {
std::vector<unsigned> sieve = make_sieve(SQSIZE);
unsigned long k, n, n_div;
unsigned N;
std::ifstream fin ("nr_div_huge.in");
std::ofstream fout("nr_div_huge.out");
fin >> N;
for(unsigned i=0; i<N; i++) {
fin >> n >> k;
n_div = n_divisors(k, n+1, sieve) * n_divisors(k + 1, 1, sieve);
fout << n_div << '\n';
}
return 0;
}
这里不需要long long
。 n
和k
小于1000000000,并且适合32位整数。因此,使用了unsigned long
。
当心:上面的代码盲目地假设没有IO问题,并且从不对其进行测试。绝对不要为生产级代码这样做...