c语言素数筛法与分解素因数,HDU - 6069 Counting Divisors(素数筛法+技巧质因数分解)...

题目的意思是:给出l,r, k,求l到r区间内每个数的k次方的约数个数的和,再对998244353取余。

这题用到的技巧还真是多啊,乱搞暴力肯定GG。

首先我们来看看怎么求解约数的个数。一个数可以分解成多个质数的的乘积。假设p为质数(1不是质数啊,怎么每次都讲不听呢),所以有:n = p1^c1*p2^c2*p3^c3*......*pm^cm

根据乘法原理:n的约数的个数就是(c1+1)(c2+1)(c3+1)…(cm+1)。所以:

d(n) = (c1+1)(c2+1)(c3+1)…(ck+1)

d(n^k) = (k*c1+1)(k*c2+1)(k*c3+1)…(k*cm+1)

本以为通过素数筛法筛了素数之后对l-r区间内每个数进行素数分解就OK了,没想到还是TLE了。这里要用到一个技巧,因为我们直接对区间的数进行分解的话,当一个数是很大的质数时就会增加很多不必要的计算判断,所以我们要将质数乘以一定的倍数使它增长到区间内,如果倍数在区间内那么直接将区间内的数分解即可,之后再以当前质数的一倍递增,这样就可以分解完区间内所有能被当前质数分解的数。并且质数之积肯定是小于等于r的。

通过上面的方法分解完成后循环一遍将每个数的d(n^k)%mod加起来取模即可。

说的可能有点繁琐,具体请看代码。

代码:StatusAccepted

Time2732ms

Memory25984kB

Length1892

LangC++

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

using namespace std;

#define lowbit(x) ((x)&(-x))

#define INF 0x3f3f3f3f

#define mem(a,b) memset(a,b,sizeof(a))

#define    GE() printf(">----------\n")

#define    IN() freopen("in.txt","r",stdin)

#define    OUT() freopen("out.txt","w",stdout)

#define MP pair

typedef long long LL;

const int mod=998244353;

const int inf=0x3f3f3f3f;

const double pi=acos(-1.0);

const double eps=1e-8;

const int maxn=1000005;

int t;

LL l, r, k, ans;

LL prime[maxn], vis[maxn], cp;

LL cnt[maxn], num[maxn], tot;

void Prime(){                        //快速素数筛法,普通素数筛法也能过,这个题我用两种筛法时间差不了多少

mem(vis, 0),cp=0;

for(int i=2;i

if(!vis[i])prime[cp++]=i;

for(int j=0;j

vis[i*prime[j]]=1;

if(i%prime[j]==0)break;

}

}

}

int main()

{

Prime();

scanf("%d", &t);

while(t--){

scanf("%lld%lld%lld", &l, &r, &k);

ans=0;

if(l==1)ans=1, l++;

tot=r-l+1;

for(int i=0;i

num[i]=l+i;         //暂存l-r区间数

cnt[i]=1;           //初始化为1,统计每个数的d(nk)

}

for(int i=0;prime[i]*prime[i]<=r;i++){      //遍历区间内所有质数

LL now=ceil(l*1.0/prime[i])*prime[i];       //计算质数最低开始数

for(now;now<=r;now+=prime[i]){          //质数递增

LL c=0;

while(num[now-l]%prime[i]==0){      //区间数分解

num[now-l]/=prime[i];

c++;

}

cnt[now-l]*=(k*c+1)%mod;

cnt[now-l]%=mod;

}

}

for(int i=0;i

if(num[i]!=1)ans+=(cnt[i]*(k+1))%mod;

else ans+=cnt[i];

ans%=mod;

}

printf("%lld\n", ans);

}

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值