【HDU 6069 Counting Divisors】 + 素数筛 & 思维

Counting Divisors

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1372 Accepted Submission(s): 494

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 :

(∑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).

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

题解 :
设n=pc11pc22...pcmmn=p,则d(nk)=(kc1+1)(kc2+1)...(kcm+1)枚举不超过r√​​ 的所有质数p,再枚举区间[l,r]中所有p的倍数,将其分解质因数,最后剩下的部分就是超过r√​​ 的质数,只可能是0个或1个。 时间复杂度O(r√+(r−l+1)loglog(r−l+1))

例如 : 对于 6 ^ 2 = 36,有 1,2,3,4,6,9,12,18,36 共 9 个因子,而 6 = 2 ^ 1 * 3 ^ 1,可以拆分成素因子相乘的形式,所以 6 ^ 2 的因子 = (1 * 2 + 1) * (1 * 2 + 1) = 9,6 ^ k = (1 * k + 1 ) * (1 * k + 1),即因子 2 的次幂 * k + 1 * 因子 3 的次幂 * k + 1, 对于 n ^ k 的因子数 = 把 n 拆分成素因子相乘的形式后,每个素因子的次幂 * k + 1,最后在相乘~

首先筛选出 1 ~ 1e6 内的素数, 题目给出 r - l <= 1e6, 可以从这个条件入手,如果直接遍历 l ~ r,内的每个数的素因数,1~1e6 有 78499 个素数,会 T, 利用 r - l <= 1e6 的条件把 l~r 压缩到一个 0 ~ (r - l)的数组里, 用素数筛的方法,用每个素数去 遍历 l ~ r 的区间,还需要一个数组记录下 l ~ r 对应位置的初始值,方便最后要判断下如果经过所有的素数整除判断后还不为 1,者这个数便是一个素数 +这个素数的贡献

AC代码:

#include<cstdio>
typedef long long LL;
const int mod = 998244353;
const int MAX = 1e6 + 10;
int vis[MAX],p[MAX],nl;
LL l,r,k,a[MAX],s[MAX];
void init(){
    nl = 0;
    for(int i = 2; i < MAX; i++){
        if(!vis[i]) p[++nl] = i;
        for(int j = 1; j <= nl; j++){
            LL o = (LL) i * p[j];
            if(o > MAX) break;
            vis[o] = 1;
        }
    }
}
void solve(){
    for(int i = 0; i <= r - l; i++) s[i] = 1,a[i] = l + i;
    for(int i = 1; i <= nl && (LL)p[i] * p[i] <= r ; i++){
        LL o = l / p[i] + (l % p[i] != 0);
        for(LL j = o * p[i]; j <= r; j += p[i]){
            LL w = 0;
            while(a[j - l] % p[i] == 0) a[j - l] /= p[i],w++;
            s[j - l] = (w * k % mod + 1) % mod * s[j - l] % mod;
        }
    }
    LL ans = 0;
    for(int i = 0; i <= r - l; i++)
        if(a[i] == 1) ans = (ans + s[i]) % mod;
        else ans = (ans + s[i] * (k + 1) % mod) % mod;
    printf("%lld\n",ans);
}
int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld %lld %lld",&l,&r,&k);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值