题目链接:https://vjudge.net/contest/362639#problem/J
In mathematics, the function d(n)d(n) denotes the number of divisors of positive integer nn.
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 :(r∑i=l d(i^k))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≤10^12,r−l≤10 ^6,1≤k≤10 ^7)l,r,k(1≤l≤r≤10 ^12,r−l≤10 ^6,1≤k≤10 ^7)
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
题意:
d(i)为i的因子数。例如:d(12)=6
给定三个数 l,r,k。求d(i ^k),i属于[l,r]。
分析:
对于一个数i(先不考虑k次方)的因子数可以通过唯一分解定理的扩展求得。
唯一分解定理:N=p1 ^a1 * p2 ^a2 * p3^a3…pn ^an(N为非素数)。
扩展:N的因子数=(a1+1)*(a2+1) …(an+1)。
若添加上k次方====>i^k的因子数:(a1k+1) * (a2k+1) * …(ank+1)。
接下来的问题是如何将long long 型的l和r进行转化。
利用r-l的差值
b[i-l]=i:用b[i-l]表示i,a[i-l] 表示i^k的因子个数。
总体思想:
- 唯一分解定理离不开求素因子。利用素数筛枚举1e6范围内的素数。
素数筛的思想:最小的数是2,删除2的倍数,接下来最小的数是3,删除3的倍数。。。。。。
void init()
{
tot=0;
memset(book,false,sizeof(book));
for(int i=2; i<=N; i++)/*素数筛*/
{
if(!book[i])
{
prime[tot++]=i;
for(int j=2*i; j<=N; j+=i)
book[j]=true;
}
}
}
- 将a,b数组初始化
for(LL i=l; i<=r; i++)
{
a[i-l]=1;/*a[i-l]:表示i的因子个数*/
b[i-l]=i;/*b[i-l]:利用r-l<=1e6的特性,用1e6的数组表示出1e12范围的数*/
}
- 枚举每一个素数的同时遍历整个[l,r]区间。找到每一个能被这个素数整除的i。
for(int i=0; i<tot&&prime[i]*prime[i]<=r; i++) /*枚举符合条件的素数*/
{
/*每次枚举一个素数,遍历一遍[l,r]区间,找能被这个素数整除的数*/
LL v=(l/prime[i])*prime[i];/*从最小的l开始,看能被分解为几个prime[i]*/
while(v<l)/*10=3*3+1的情况*/
v+=prime[i];
while(v<=r)
{
LL cnt=0;
while(b[v-l]%prime[i]==0)
{
cnt++;
b[v-l]/=prime[i];
}
cnt=(cnt*k+1)%mod;/*唯一分解定理的推广*/
a[v-l]=(a[v-l]*cnt)%mod;
v+=prime[i];
}
}
- 输出结果
LL sum=0;
for(LL i=l; i<=r; i++)
{
if(b[i-l]==1)
sum=(sum+a[i-l])%mod;
else/*剩余的一个数是素数*/
sum=(sum+a[i-l]*(k+1))%mod;
}
printf("%lld\n",sum);
完整代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e6+100;
const int mod=998244353;
LL l,r,k;
bool book[N];
LL a[N],b[N];
int prime[N],tot;
void init()
{
tot=0;
memset(book,false,sizeof(book));
for(int i=2; i<=N; i++)/*素数筛*/
{
if(!book[i])
{
prime[tot++]=i;
for(int j=2*i; j<=N; j+=i)
book[j]=true;
}
}
}
void solve()
{
for(LL i=l; i<=r; i++)
{
a[i-l]=1;/*a[i-l]:表示i的因子个数*/
b[i-l]=i;/*b[i-l]:利用r-l<=1e6的特性,用1e6的数组表示出1e12范围的数*/
}
for(int i=0; i<tot&&prime[i]*prime[i]<=r; i++) /*枚举符合条件的素数*/
{
/*每次枚举一个素数,遍历一遍[l,r]区间,找能被这个素数整除的数*/
LL v=(l/prime[i])*prime[i];/*从最小的l开始,看能被分解为几个prime[i]*/
while(v<l)/*10=3*3+1的情况*/
v+=prime[i];
while(v<=r)
{
LL cnt=0;
while(b[v-l]%prime[i]==0)
{
cnt++;
b[v-l]/=prime[i];
}
cnt=(cnt*k+1)%mod;/*唯一分解定理的推广*/
a[v-l]=(a[v-l]*cnt)%mod;
v+=prime[i];
}
}
}
int main()
{
int t;
scanf("%d",&t);
init();
while(t--)
{
scanf("%lld%lld%lld",&l,&r,&k);
solve();
LL sum=0;
for(LL i=l; i<=r; i++)
{
if(b[i-l]==1)
sum=(sum+a[i-l])%mod;
else/*剩余的一个数是素数*/
sum=(sum+a[i-l]*(k+1))%mod;
}
printf("%lld\n",sum);
}
return 0;
}