2017 Multi-University Training Contest - Team 4 :Counting Divisors(数论:素数筛选+分解质因子+求因子数)

点击打开题目链接

先打表筛选1~1e6内的素数,L,R最多到1e12,然后一个数是素数是这样定义的,如果n是素数,则2~(int)sqrt(n),都不能被n整除。

所以我们筛选素数只需要筛选到sqrt(1e12)范围就好。

其次,任何一个正整数x,都可以分解成若干个素数幂的积。小学学的分解质因子的方法就是这样的。

则 x = (p1^m1)*(p2^m2)*(p3^m3)*.....*(pn^mn);    其中p1,p2,p3...pn都是素数,m1,m2,m3...mn都是幂指数。

则 x 的因子个数d(x) = (m1+1)*(m2+1)*(m3+1)....(mn+1);

这和这些素因子的排列组合有关,举个例子:

例如    12  = 2×2×3

则     12的因子个数为  1 ×(2+1)×(1+1) = 3*2 = 6    

12的因子有: 1   2  3   4  6  12  可以看出答案是正确的。那么为什么会是这样的。

这主要是和这些质因子的组合有关。12 由两个2,和一个3连乘得到

则组合情况有:0个2和0个3乘    1

                         1个2和0个3乘    2

                         2个2和0个3乘    4

                         0个2和1个3乘    3

                         1个2和1个3乘    6

                         2个2和1个3乘   12

对于x^k = ((p1^m1)*(p2^m2)*(p3^m3)*.....*(pn^mn))^k;

       x^k = (p1^m1*k)*(p2^m2*k)*(p3^m3*k)*.....*(pn^mn*k);

则x^k的因子个数d(x^k) = (m1*k+1)*(m2*k+1)*(m3*k+1)*.....*(mn*k+1);    

因为读过《基础数论》这本书,这些结论都知道,就是比赛的时候,求完素数表示,一直都时对每一个数进行分解质因数,

最后导致一直超时,方法不合适,怎么改最后都超时,比赛后看他们都是对某个素数,直接将其倍数直接筛过,这样的确快很多,

只怪自己太笨,不懂得变通,又签到了一场比赛。哭


AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>

using namespace std;

typedef long long LL;

const int mod = 998244353;
const int maxn = 1000100;
int prime[maxn];  ///素数表
LL a[maxn];
LL yinzi[maxn];
int num;
void create_table()
{
    memset(a,0,sizeof(a));  ///0代表是素数
    for(int i = 2; i <= sqrt(maxn); i++)
    {
        for(int j = 2*i; j <= maxn; j+=i)
        {
                if(a[j]==0)
                    a[j] = 1;
        }
    }
    num = 0;
    for(int i = 2; i <= maxn; i++)
    {
        if(a[i]==0)
        {
            prime[num++] = i;
        }
    }
}
/**由于left和right值比较大,所以存他们的
相关信息存不下,而left和right差距不超过1e6,
因此hash一下,将其散列到0~1e6内**/
void Hash(LL left,LL right)
{
    ///
    for(LL i = left; i <= right; i++)
    {
        yinzi[i-left] = 1;
        a[i-left] = i;
    }
}
void solve(LL left,LL right,int k)
{
    for(int i = 0; i<num && prime[i]<=sqrt(right*1.0); i++)
    {
        LL start,ccount;
        if(left%prime[i]==0) start = left;
        else start = left/prime[i]*prime[i]+prime[i];  ///求起点
        for(LL j = start; j <= right; j += prime[i])
        {
            ccount = 0;  ///记录prime[i]的幂
            while(a[j-left]%prime[i]==0)
            {
                ccount++;
                a[j-left] = a[j-left]/prime[i];
            }
            yinzi[j-left] = ((yinzi[j-left]%mod)*((ccount*k+1)%mod))%mod;
        }
    }
    LL ans = 0;
    for(int i = 0; i <= right-left; i++)
    {
        if(a[i]!=1)  ///代表是素数
        {
            ans = (ans+(yinzi[i]*(k+1))%mod)%mod;
        }
        else
        {
            ans = (ans+yinzi[i])%mod;
        }
    }
    cout<<ans<<endl;
}
int main()
{
   int t,k;
   LL left,right;
   create_table();  ///打素数表
   cin>>t;
   while(t--)
   {
       cin>>left>>right>>k;
       Hash(left,right);
       solve(left,right,k);
   }
   return 0;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值