HDU6069(素数筛选+思维)

Counting Divisors

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

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

解题思路:这是今天的多校题,比赛时没有做出来,看了题解才做出来。

我们知道一个数可以分解为很多质数的乘积,如下图:
这里写图片描述
然后我们肯定要把每个数进行素数分解,比赛时我也想到了这一点,但是这题的l, r到了1e12,然后比赛时对于大于1e6次方的素数(我们也不知道大于1e6的数是不是一个素数),进行素数判定,用的是米勒拉宾素数测试,然后一直t,看了题解之后才知道,根本不需要对大于1e6的数进行素数测试,我们可以用1 - 1e6之间的素数(这些可以用素数筛法处理出来),去筛大于1e6的数,举个简单的例子,比如现在我们有素数2,然后l, r中是2的倍数都不是素数,这样的话我们可以枚举1-1e6之间所有的素数(其实可以不用枚举所有,我们可以用二分处理出sqrt(r)左右的素数),去筛l, r之间的数,筛完之后剩下的就是素数,中间的值我们用几个数组记录下就行,最后加起来就是答案。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const ll mod = 998244353;
ll l, r, k;
ll prime[maxn];
ll p2[maxn];//素数的平方,用于二分找出最小的s满足prime[s] * prime[s] >= r
ll ans[maxn];
int res;
bool valid[maxn];
ll result[maxn];
ll num[maxn];
bool visit[maxn];
void getPrime()
{
    int tot = 0;
    memset(valid, true, sizeof(valid));
    for(ll i = 2; i < 1000000; i++)
    {
        if(valid[i])
        {
            ++tot;
            ans[tot] = i;
        }
        for(ll j = 1; ((j <= tot) && (i * ans[j] < 1000000)); j++)
        {
            valid[i * ans[j]] = false;
            if(i % ans[j] == 0) break;
        }
    }
    res = 0;
    for(ll i = 2; i < 1000000; i++)
    {
        if(valid[i]) prime[++res] = i;
        p2[res] = prime[res] * prime[res];
    }
}
int main()
{
    int T;
    getPrime();//筛出1e6以内的素数
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lld%lld%lld", &l, &r, &k);
        memset(visit, false, sizeof(visit));
        for(ll i = 1, j = l; j <= r; i++, j++)
        {
            num[i] = j;
            result[i] = 1;
        }
        int L = 1;
        int R = res;
        int s = 1;
        while(L <= R)
        {

            int mid = (L + R)>>1;
            if(r >= p2[mid])
            {
                s = mid;
                L = mid + 1;
            }
            else R = mid - 1;
        }
        s = res;
        for(int i = 1; i <= s; i++)
        {
            ll pri = prime[i];
            ll judge = l / pri;
            int xx;
            if(pri * judge == l)
            {
                xx = l;
            }
            else
            {
                xx = (judge + 1) * pri;
            }
            for(int j = xx - l + 1; j <= r - l + 1; j += pri)
            {
                ll yy = 0;
                while(num[j] % pri == 0)
                {
                    yy++;
                    num[j] /= pri;
                }
                if(yy)
                {
                    result[j] = (result[j] * (k * yy%mod + 1)%mod)%mod;
                    visit[j] = true;
                }

            }
        }
        ll sum = 0;
        for(int i = 1; i <= r - l + 1; i++)
        {
            if(!visit[i])
            {
                if(num[i] == 1) sum = (sum + 1) % mod;
                else sum = (sum + k + 1) % mod;
            }
            else
            {
                if(num[i] == 1) sum = (sum + result[i]) % mod;
                else sum = (sum + result[i] * (k + 1) % mod) % mod;
            }
        }
        printf("%lld\n", sum);

    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值