题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6069
题意:
设d(xk)为求xk的因数的个数,如12的因数有1,2,3,4,6,12共6个,d(12)=6,题目求∑i=lrd(ik)
官方题解:
设n=pc11pc22...pcmm,则d(nk)=(kc1+1)(kc2+1)...(kcm+1)
枚举不超过r√的所有质数p,再枚举区间[l,r]中所有p的倍数,将其分解质因数,最后剩下的部分就是超过r√的质数,只可能是0个或1个。
总结:
如果对区间内每一个数都进行质数分解,写得挫一点每个数的复杂度是O(n√),写得好一点每次枚举素数,但是即使这样比赛时还是超时了。
题解就是用O(r√)的复杂度枚举[1,r√]内的素数,用这个范围内的素数去筛[l,r]内的数,这样就省去了枚举到的素数不是这个数的因数的时间。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
const int mod = 998244353;
bool valid[maxn];
int ans[maxn], tot;
ll mp[maxn], d[maxn];
void getPrime(int n, int &tot, int ans[]){
tot = 0;
memset(valid, true, sizeof(valid));
valid[0] = valid[1] = false;
for(int i=2; i<=n; ++i){
if(valid[i]){
ans[++tot] = i;
}
for(int j=1; ((j<=tot)&&(i*ans[j]<=n)); ++j){
valid[i*ans[j]] = false;
if(i%ans[j]==0) break;
}
}
}
ll solve(ll l, ll r, ll k){
for(int i=1; i<=tot; ++i){
ll idx = l / ans[i] + (l%ans[i]==0?0:1);
idx = idx * ans[i];//第一个大于等于l并且是素数ans[i]的倍数的数
if(idx > r) break;
while(idx <= r){
ll cnt = 0;
while(mp[idx-l] % ans[i] == 0){
mp[idx-l] /= ans[i];
++cnt;
}
d[idx-l] = d[idx-l] * ((cnt*k+1) % mod) % mod;
idx += ans[i];
}
}
ll ret = 0;
for(ll i=0; i<=r-l; ++i){
if(mp[i]==1)
ret += d[i];
else
ret += d[i] * (k+1);
ret %= mod;
}
return ret;
}
int main(){
ll t, l, r, k;
ios::sync_with_stdio(false);
cin>>t;
getPrime(maxn-5, tot, ans);
while(t--){
cin>>l>>r>>k;
for(ll i=l; i<=r; ++i){
d[i-l] = 1;
mp[i-l] = i;
}
cout<<solve(l, r, k)<<endl;
}
return 0;
}