题面:整数分块again

大秦 为你打开 题目传送门

备用题面

看不了题目的看下面喵!

给出 $N$ 并计算下面表达式的值:

答案对于 $M$ 取模。

思路

拿到题面,注意到整数分块的经典部分,不难写出如下代码:

1
2
3
4
5
6
7
8
9
inline void Work() {
ll ans = 0;
for(ll l = 1, r = 1; r <= n; l = r + 1) {
r = n / (n / l);
(ans += ((f(r) - f(l - 1) + P) % P) * (n / l) % P) %= P;
if(r == n) break;
}
printf("%lld\n", ans);
}

那么这题主要的考点,其实就是计算一段区间内的四次方和。借助强大的搜索引擎我们可以轻松知道四次方和前缀和公式是:

当我看到这个式子的时候,想都没有想直接上逆元,因为众所周知取模除法可以表示为 $a\div b\equiv a\times b^{-1}\pmod P$ 其中根据费马小可以知道 $b^{-1}\equiv b^{P-2}$ 当然前提是 $P$ 是质数。不过非常遗憾,这道题关于模数并没有好的性质,比如说 $P$ 是质数啊,$30\perp P$ 啊之类的,他全都没有,所以说用逆元来计算这玩意也不太行。

事实上可以用 $30\times P$ 代替模数进行运算,接下来给出关于这个代替方式的证明以及一些要求。

首先抽象一下题目意思,目前要计算的问题是 $(a\div b)\bmod P$ 的结果。

第一步我们可以根据整数除法,将 $a$ 拆分为 $a=k\times (bP)+r\ (0\le r\lt bP)$ 。
然后我们分开计算 $(a\div b)\bmod P$ 和 $((a\bmod bP)\div b)\bmod P$。

最后相等了。但是有一个要注意的地方是,$b\mid r$ 也就是 $b$ 可以整除 $r$(不然的话分数不能直接参与模运算,你还是得算逆元……)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
// typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

ll n, P;

inline void Input() {
read(n, P);
}

inline ll Pow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % P;
a = a * a % P;
b = b >> 1;
}
return ans;
}

ll P2, f1, f2, f3, f4;

inline ll f(ll n) {
P2 = 30 * P;
f1 = n % P2;
f2 = (n + 1) % P2;
f3 = (n * 2 + 1) % P2;
f4 = ((3 * n % P2 * n + 3 * n % P2) % P2 - 1 + P2) % P2;
return ((f1 * f2 % P2 * f3 % P2 * f4 % P2) / 30) % P;
}

inline void Work() {
ll ans = 0, lst = 0, now = 0;
for(ll l = 1, r = 1; r <= n; l = r + 1) {
r = n / (n / l); now = f(r);
(ans += ((now - lst + P) % P) * (n / l) % P) %= P;
lst = now;
if(r == n) break;
}
printf("%lld\n", ans);
}

int main() {
int T = read(), ca = 0;
while(T--) {
Input();
// printf("Case %d: ", ++ca);
Work();
}
return 0;
}