HDU 6069 Counting Divisors【素数筛】【约数个数定理】

题目来戳呀

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 :

( ri=ld(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).

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,求 ik 的因子个数之和。(l<i<r)

想法:

首先,有这么你不知道就做不出来这道题的定理:)
约数个数定理
对于一个大于1正整数n可以分解质因数
如果它的标准分解式为:N=P a11 P a22 ……P ann
那么它的正因数个数为f(N)=(1+ a1 )·(1+ a2 )·⋯·(1+ an ),
所以f( Nk )=(1+ ka1 )·(1+ ka2 )·⋯·(1+ kan )

我们只要想办法把每个数分解成质因数再运用定理公式累加即可。
但是单独的把一个数直接分很困难,所以我们来枚举(l,r)内小于 r 素数,来判断这个素数(包括其倍数)是不是这个数的素因子。若是就进入约数个数定理的函数,不是就不进入呗~

tips:
1.选出这些素数运用的是素数筛。
2.注意题目要求 不断取余。
3.约数个数定理中注意i的初始化,以及走完这个函数后,最后还要处理一下仍未被分解完成的数。

#include<cstdio>
using namespace std;
const int mod=998244353;
const int maxn=1e6+10;
typedef long long ll;
ll f[maxn],g[maxn],su[maxn/10];//f[i]表示每个区间内的底数 g[i]是每个指数的因子个数 su[i]表示每个素因子
ll cnt,l,r,k;
bool vis[maxn];
void solve(ll p)//约数个数定理 每次传入一个素数 判断这个数是不是这个数的素因子
{
    for(ll i=l/p*p;i<=r;i+=p)//注意i的初始化 i是指区间第几个数
    {///这里令i先除以p,再乘以p,就可以在小于等于L的区间里找到第一个能被p整除的数(重点)。
         if(i>=l)
        {
            int mi=0;///表示p的指数
            while(f[i-l]%p==0)
            {
                f[i-l]/=p;
                mi++;
            }
            g[i-l]=1LL*g[i-l]*(1+mi*k)%mod;//这里因为忘了取余错了QAQ
        }

    }
}
int main()
{
    cnt=0;
    for(ll i=2;i<maxn;++i)//素数筛
    {
        if(!vis[i])
            su[cnt++]=i;//cnt是素数个数
        for(ll j=0;j<cnt&&i*su[j]<maxn;j++)
        {
            vis[i*su[j]]=1;
            if(i%su[j]==0)
                break;
        }
    }
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld",&l,&r,&k);
        int n=r-l+1;//区间内总个数
        for(int i=0;i<n;++i)
        {
            f[i]=l+i;///f[i]表示从左往右第i个底数
            g[i]=1;
        }
        for(int i=0;i<cnt;++i)
        {
            if(1LL*su[i]*su[i]>r)//若质数平方大于右边界 直接跳出
            {
                break;
            }
            solve(su[i]);
        }
        ll ans=0;
        for(int i=0;i<n;++i)
        {///在都进入过约数个数定理函数分解后
            if(f[i]>1)///讨论没有被质数除尽的,和本身是大于根号r的大素数
            {
                g[i]=1LL*(1+k)*g[i]%mod;
            }
            ans=(ans+g[i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

ps:会了定理就会了 可是我不知道定理(手动微笑)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值