A⩽B⩽5000的算法
没有什么技术含量的吧..
只需要暴力预处理50005000以内的逆元,然后直接暴力计算答案,记录一个前缀和就可以O(1)O(1)完成询问了.
T = 1T=1的算法
首先一个询问求的东西就是这个:\sum_{i=A}^{B}\sum_{j=1}^{i}\lbrace\frac{i}{j}\rbrace \qquad (\bmod\ 998244353)i=A∑Bj=1∑i{ji}(mod 998244353)
然后我们可以把它变个形:
\sum_{i=A}^{B}\sum_{j=1}^{i}(\frac{i}{j}-\lfloor\frac{i}{j}\rfloor)\qquad(\bmod\ 998244353)i=A∑Bj=1∑i(ji−⌊ji⌋)(mod 998244353)
\sum_{i=A}^{B}\sum_{j=1}^{i}\frac{i}{j}-\sum_{i=A}^{B}\sum_{j=1}^{i}\lfloor\frac{i}{j}\rfloor\qquad(\bmod\ 998244353)i=A∑Bj=1∑iji−i=A∑Bj=1∑i⌊ji⌋(mod 998244353)
\sum_{i=1}^{B}\sum_{j=max(A,i)}^{B}\frac{j}{i}-\sum_{i=1}^{B}\sum_{j=A}^{B}\lfloor\frac{j}{i}\rfloor\qquad(\bmod\ 998244353)i=1∑Bj=max(A,i)∑Bij−i=1∑Bj=A∑B⌊ij⌋(mod 998244353)
\sum_{i=1}^{B}\sum_{j=max(A,i)}^{B}\frac{j}{i}-\sum_{i=1}^{B}(\sum_{j=0}^{B}\lfloor \frac{j}{i} \rfloor-\sum_{j=0}^{A-1}\lfloor \frac{j}{i} \rfloor)\qquad(\bmod\ 998244353)i=1∑Bj=max(A,i)∑Bij−i=1∑B(j=0∑B⌊ij⌋−j=0∑A−1⌊ij⌋)(mod 998244353)
然后我们只需要分别求出左右两边的值就好了.
前面一半我们只需要枚举ii,然后里面的jj可以用高斯求和公式直接算出来,然后乘上一个ii的逆元就好.
后面一半的话..
我们先拿i=3的情况玩玩:
0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5...
发现了什么??
如果不太明白,再多拿一些数搞一搞,你就会发现这个规律了..
然后后面的一样只需要枚举ii,后面的东西也可以用高斯求和公式算.
这样就有50分了.
75分算法
把前面两个拼起来就好了.
100分算法
首先我们还是用T=1T=1的思路,把这个式子拆成这个样子:
\sum_{i=A}^{B}\sum_{j=1}^{i}\frac{i}{j}-\sum_{i=A}^{B}\sum_{j=1}^{i}\lfloor\frac{i}{j}\rfloor\qquad(\bmod\ 998244353)i=A∑Bj=1∑iji−i=A∑Bj=1∑i⌊ji⌋(mod 998244353)有没有可以直接预处理的办法呢??
答案当然是有的..
对于前一个式子,我们只需要预处理11~BB的逆元,然后求一个前缀和,我们发现ii的每一次增加,实际上就是加上11~i-1i−1的逆元+11(\frac{i}{i}ii).这里可以好好思考一下.
所以我们只需要用线性求逆元的方法,求一遍前缀和后计算每一个ii的里面的\sum^i_{j=1}\frac{i}{j}∑j=1iji的值,然后再求一遍前缀和我们就可以O(1)O(1)求得前面一块的任意一对A,B的值了.
然后再考虑后面一块.
首先我们可以考虑\lfloor\frac{i}{j}\rfloor⌊ji⌋的更多的含义.
这个等同于求出小于等于ii的数中有多少个数是jj的倍数.
然后拓展到\sum_{j=1}^{i}\lfloor\frac{i}{j}\rfloor∑j=1i⌊ji⌋呢?
这个就是求小于等于ii的数中有多少是11的倍数,22的倍数,33的倍数...ii的倍数.
也就是说,对于每一个数,它对这个答案就有它的因子个数个贡献.
所以我们就可以得到这样一个等式:
\sum_{j=1}^{i}\lfloor\frac{i}{j}\rfloor=\sum_{j=1}^{i}d(i)j=1∑i⌊ji⌋=j=1∑id(i)
其中d(i)d(i)表示ii的因子个数.
然后考虑如何快速求11~nn的因子个数.
这个就是积性函数了..可以使用欧拉线行筛
求出d(i)d(i)之后,我们就只需要求两次前缀和就好了..这个可以直接把上面的等式代入原式就可以看出来了..
这样子这道题就解决了..
代码如下(请原谅蒟蒻丑陋的码风):
#include <bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
inline int read() {
reg int s = 0; reg bool t = 0; reg char ch = getchar();
while(!isdigit(ch)) t |= ch == '-', ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return t ? -s : s;
}
const char FI[] = "dinner.in";
const char FO[] = "dinner.out";
const int N = 1000000;
const int mod = 998244353;
int T;
int A;
int B;
int d[N + 1];
int s[N + 1];
int f[N + 1];
int ins[N + 1] = {0, 1};
int tot;
int prime[N + 1];
bitset<N + 1>Check;
void init() {
d[1] = 1;
for(reg int i = 2, tmp = 2; i <= N; i++, tmp = i) {
if(!Check[i]) prime[++tot] = i, d[i] = 2;
for(reg int j = 1, pr = prime[j], s = 2; j <= tot && i * pr <= N; pr = prime[++j]) {
Check[i * pr] = 1;
if(i % pr == 0) {
while(tmp % pr == 0) tmp /= pr, s++;
d[i * pr] = d[tmp] * s;
break;
}
d[i * pr] = d[i] << 1;
}
}
for(reg int i = 1; i <= N; i++) s[i] = s[i - 1] + d[i], s[i] -= s[i] >= mod ? mod : 0;
for(reg int i = 1; i <= N; i++) s[i] += s[i - 1], s[i] -= s[i] >= mod ? mod : 0;
for(reg int i = 2; i <= N; i++) ins[i] = 1LL * (mod - mod / i) * ins[mod % i] % mod;
for(reg int i = 2; i <= N; i++) ins[i] += ins[i - 1], ins[i] -= ins[i] >= mod ? mod : 0;
for(reg int i = 1; i <= N; i++) f[i] = f[i - 1] + ins[i - 1] + 1, f[i] -= f[i] >= mod ? mod : 0;
for(reg int i = 1; i <= N; i++) f[i] += f[i - 1], f[i] -= f[i] >= mod ? mod : 0;
}
int main() {
init();
reg int T = read();
for(reg int Q_Q = 0; Q_Q < T; Q_Q++) {
A = read() - 1, B = read(),
printf("%d\n", ((f[B] - f[A] - s[B] + s[A]) % mod + mod) % mod);
}
return 0;
}