Counting Divisors
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Problem Description
In mathematics, the function
d(n)
denotes the number of divisors of positive integer
n
.
For example, d(12)=6 because 1,2,3,4,6,12 are all 12 's divisors.
In this problem, given l,r and k , your task is to calculate the following thing :
For example, d(12)=6 because 1,2,3,4,6,12 are all 12 's divisors.
In this problem, given l,r and k , your task is to calculate the following thing :
(∑i=lrd(ik))mod998244353
Input
The first line of the input contains an integer
T(1≤T≤15)
, denoting the number of test cases.
In each test case, there are 3 integers l,r,k(1≤l≤r≤1012,r−l≤106,1≤k≤107) .
In each test case, there are 3 integers l,r,k(1≤l≤r≤1012,r−l≤106,1≤k≤107) .
Output
For each test case, print a single line containing an integer, denoting the answer.
Sample Input
3 1 5 1 1 10 2 1 100 3
Sample Output
10 48 2302
Source
2017 Multi-University Training Contest - Team 4
题目大意
已知l,r,k,i为区间 [ l , r ] 中的任意整数,求所有 i^k 的因子个数的和。
题目分析
首先我们要知道两条性质:
1.对于 每个大于1的 自然数, 都可以分解为质数的 积的形式 ,并且这种形式是唯一的 。举个栗子:6936 = 2^3*3*17^2。
其中,p1、p2、p3,…pk为互异质数,其中a1、a2、a3…ak是p1、p2、p3,…pk的指数。
另外,对于一个数i^k,它的约数个数是(a1*k+1)(a2*k+1)……(ai*k+1)。
这样,问题就转化成,对一个数i的质因数分解,一开始的思路是首先筛出小于10^6所有素数,然后进行质因数分解,结果超时了。赛后看题解,标程的做法是,设置了两个数组f[ ](存区间[ l , r ]的所有数),g[ ](存对应f[ ]的约数个数的计算结果),枚举小于10^6的所有质数,针对枚举的质数p,分解所有p的倍数的以p作为质因子的项,(注意,数组f[ ]和g[ ]存的都是分解到当前质因子p的结果,详情请参考代码函数work( ))。
标程的方法快在,只需要遍历一边质数集合就将区间中的所有数都分解好了,而不是区间中的每个数都分别进行因数分解。
最后,需要提醒一下,对于一个数i的质因数分解,最终剩下的部分可能是1或者是一个大于sqrt(i)的质数,所以,在最后,如果f[i]>1,我们需要将这个质因子加到最终结果中。
另附官方题解
代码
题目大意
已知l,r,k,i为区间 [ l , r ] 中的任意整数,求所有 i^k 的因子个数的和。
题目分析
首先我们要知道两条性质:
1.对于 每个大于1的 自然数, 都可以分解为质数的 积的形式 ,并且这种形式是唯一的 。举个栗子:6936 = 2^3*3*17^2。
2.这个数n的约数的个数是:(接上一个栗子:f(6936) = (3+1)*(1+1)*(2+1) = 24)
另外,对于一个数i^k,它的约数个数是(a1*k+1)(a2*k+1)……(ai*k+1)。
这样,问题就转化成,对一个数i的质因数分解,一开始的思路是首先筛出小于10^6所有素数,然后进行质因数分解,结果超时了。赛后看题解,标程的做法是,设置了两个数组f[ ](存区间[ l , r ]的所有数),g[ ](存对应f[ ]的约数个数的计算结果),枚举小于10^6的所有质数,针对枚举的质数p,分解所有p的倍数的以p作为质因子的项,(注意,数组f[ ]和g[ ]存的都是分解到当前质因子p的结果,详情请参考代码函数work( ))。
标程的方法快在,只需要遍历一边质数集合就将区间中的所有数都分解好了,而不是区间中的每个数都分别进行因数分解。
最后,需要提醒一下,对于一个数i的质因数分解,最终剩下的部分可能是1或者是一个大于sqrt(i)的质数,所以,在最后,如果f[i]>1,我们需要将这个质因子加到最终结果中。
另附官方题解
设n=p1c1p2c2...pmcm,则d(nk)=(kc1+1)(kc2+1)...(kcm+1)。
枚举不超过r\sqrt{r}的所有质数pp,再枚举区间[l,r][l,r]中所有pp的倍数,将其分解质因数,最后剩下的部分就是超过r\sqrt{r}的质数,只可能是00个或1个。
时间复杂度O(r+(r−l+1)loglog(r−l+1))O(\sqrt{r}+(r-l+1)\log\log(r-l+1))。
代码
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1000010;
const int mod = 998244353;
int cas, k;
ll l, r, n;//n为区间长度
ll f[N];//记录区间l~r之间的所有数,并且用于素数分解操作
int g[N];//记录f[]中所对应的数的分解素数的(指数*k+1)的累乘积
int tot;//总质数的个数
int prime[N/10];//记录所有的质数
bool vis[N];//1为合数,0为质数
int i, j;
ll ans;
int cnt;
void isprime()
{
tot = 0;
//memset(vis, 0, sizeof(vis));
for (i=2; i<N; i++)
{
if (!vis[i])
prime[tot++] = i;
for (j=0; j<tot&&i*prime[j]<N; j++)
{
vis[i*prime[j]] = 1;
if (i%prime[j] == 0)
break;
}
}
}
inline void work(ll p)
{
for (ll i=l/p*p; i<=r; i+=p)//i=p-(l%p),求p的第一个倍数的位置
{
if(i>=l)
{
cnt = 0;//p的指数
while (f[i-l] && f[i-l]%p == 0)
{
f[i-l] /= p;
cnt ++;
}
g[i-l] = 1LL*g[i-l]*(cnt*k+1)%mod;
}
}
}
int main()
{
isprime();
//for (i=0; i<tot; ++i)
//printf ("%d\n",prime[i]);
scanf ("%d",&cas);
while (cas--)
{
scanf("%lld %lld %d",&l,&r,&k);
n = r-l;
for (i=0; i<=n; ++i)
{
f[i] = i+l;
g[i] = 1;
}
for (i=0; i<tot; ++i)
{
if (1LL*prime[i]*prime[i] > r)
break;
work(prime[i]);
}
ans = 0;
for (i=0; i<=n; i++)
{
if(f[i] > 1)
g[i] = 1LL*g[i]*(1*k+1)%mod;
ans = (ans+g[i])%mod;
}
printf ("%lld\n",ans);
}
return 0;
}